1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Redefine '$' here rather than including 'cr.js', since this is 6 // the only function needed. This allows this file to be loaded 7 // in a browser directly for layout and some testing purposes. 8 var $ = function(id) { return document.getElementById(id); }; 9 10 /** 11 * A generic WebUI for configuring preference values used by Chrome's gesture 12 * recognition systems. 13 * @param {string} title The user-visible title to display for the configuration 14 * section. 15 * @param {string} prefix The prefix for the configuration fields. 16 * @param {!Object} fields An array of fields that contain the name of the pref 17 * and user-visible labels. 18 */ 19 function GeneralConfig(title, prefix, fields) { 20 this.title = title; 21 this.prefix = prefix; 22 this.fields = fields; 23 } 24 25 GeneralConfig.prototype = { 26 /** 27 * Sets up the form for configuring all the preference values. 28 */ 29 buildAll: function() { 30 this.buildForm(); 31 this.loadForm(); 32 this.initForm(); 33 }, 34 35 /** 36 * Dynamically builds web-form based on the list of preferences. 37 */ 38 buildForm: function() { 39 var buf = []; 40 41 var section = $('section-template').cloneNode(true); 42 section.removeAttribute('id'); 43 var title = section.querySelector('.section-title'); 44 title.textContent = this.title; 45 46 for (var i = 0; i < this.fields.length; i++) { 47 var field = this.fields[i]; 48 49 var row = $('section-row-template').cloneNode(true); 50 row.removeAttribute('id'); 51 52 var label = row.querySelector('.row-label'); 53 var input = row.querySelector('.input'); 54 var units = row.querySelector('.row-units'); 55 var reset = row.querySelector('.row-reset'); 56 57 label.setAttribute('for', field.key); 58 label.innerHTML = field.label; 59 input.id = field.key; 60 input.min = field.min || 0; 61 62 if (field.max) 63 input.max = field.max; 64 65 input.step = field.step || 'any'; 66 67 if (field.units) 68 units.innerHTML = field.units; 69 70 reset.id = field.key + '-reset'; 71 gesture_config.updateResetButton(reset, true); 72 73 section.querySelector('.section-properties').appendChild(row); 74 } 75 $('gesture-form').appendChild(section); 76 }, 77 78 /** 79 * Initializes the form by adding appropriate event listeners to elements. 80 */ 81 initForm: function() { 82 for (var i = 0; i < this.fields.length; i++) { 83 var field = this.fields[i]; 84 var config = this; 85 $(field.key).onchange = (function(key) { 86 config.setPreferenceValue(key, $(key).value); 87 gesture_config.updateResetButton($(key + '-reset'), false); 88 gesture_config.updateResetAllButton(false); 89 }).bind(null, field.key); 90 $(field.key + '-reset').onclick = (function(key) { 91 config.resetPreferenceValue(key); 92 }).bind(null, field.key); 93 } 94 }, 95 96 /** 97 * Requests preference values for all the relevant fields. 98 */ 99 loadForm: function() { 100 for (var i = 0; i < this.fields.length; i++) 101 this.updatePreferenceValue(this.fields[i].key); 102 }, 103 104 /** 105 * Handles processing of "Reset All" button. 106 * Causes all form values to be updated based on current preference values. 107 * @return {boolean} Returns false. 108 */ 109 onReset: function() { 110 for (var i = 0; i < this.fields.length; i++) { 111 var field = this.fields[i]; 112 this.resetPreferenceValue(field.key); 113 } 114 return false; 115 }, 116 117 /** 118 * Requests a preference setting's value. 119 * This method is asynchronous; the result is provided by a call to 120 * updatePreferenceValueResult. 121 * @param {string} prefName The name of the preference value being requested. 122 */ 123 updatePreferenceValue: function(prefName) { 124 chrome.send('updatePreferenceValue', [this.prefix + prefName]); 125 }, 126 127 /** 128 * Sets a preference setting's value. 129 * @param {string} prefName The name of the preference value being set. 130 * @param {value} value The value to be associated with prefName. 131 */ 132 setPreferenceValue: function(prefName, value) { 133 chrome.send('setPreferenceValue', 134 [this.prefix + prefName, parseFloat(value)]); 135 }, 136 137 /** 138 * Resets a preference to its default value and get that callback 139 * to updatePreferenceValueResult with the new value of the preference. 140 * @param {string} prefName The name of the requested preference. 141 */ 142 resetPreferenceValue: function(prefName) { 143 chrome.send('resetPreferenceValue', [this.prefix + prefName]); 144 } 145 }; 146 147 /** 148 * Returns a GeneralConfig for configuring gestures.* preferences. 149 * @return {object} A GeneralConfig object. 150 */ 151 function GestureConfig() { 152 /** The title of the section for the gesture preferences. **/ 153 /** @const */ var GESTURE_TITLE = 'Gesture Configuration'; 154 155 /** Common prefix of gesture preferences. **/ 156 /** @const */ var GESTURE_PREFIX = 'gesture.'; 157 158 /** List of fields used to dynamically build form. **/ 159 var GESTURE_FIELDS = [ 160 { 161 key: 'fling_max_cancel_to_down_time_in_ms', 162 label: 'Maximum Cancel to Down Time for Tap Suppression', 163 units: 'milliseconds', 164 }, 165 { 166 key: 'fling_max_tap_gap_time_in_ms', 167 label: 'Maximum Tap Gap Time for Tap Suppression', 168 units: 'milliseconds', 169 }, 170 { 171 key: 'long_press_time_in_seconds', 172 label: 'Long Press Time', 173 units: 'seconds' 174 }, 175 { 176 key: 'semi_long_press_time_in_seconds', 177 label: 'Semi Long Press Time', 178 units: 'seconds', 179 step: 0.1 180 }, 181 { 182 key: 'max_seconds_between_double_click', 183 label: 'Maximum Double Click Interval', 184 units: 'seconds', 185 step: 0.1 186 }, 187 { 188 key: 'max_separation_for_gesture_touches_in_pixels', 189 label: 'Maximum Separation for Gesture Touches', 190 units: 'pixels' 191 }, 192 { 193 key: 'max_swipe_deviation_ratio', 194 label: 'Maximum Swipe Deviation', 195 units: '' 196 }, 197 { 198 key: 'max_touch_down_duration_in_seconds_for_click', 199 label: 'Maximum Touch-Down Duration for Click', 200 units: 'seconds', 201 step: 0.1 202 }, 203 { 204 key: 'max_touch_move_in_pixels_for_click', 205 label: 'Maximum Touch-Move for Click', 206 units: 'pixels' 207 }, 208 { 209 key: 'max_distance_between_taps_for_double_tap', 210 label: 'Maximum Distance between two taps for Double Tap', 211 units: 'pixels' 212 }, 213 { 214 key: 'min_distance_for_pinch_scroll_in_pixels', 215 label: 'Minimum Distance for Pinch Scroll', 216 units: 'pixels' 217 }, 218 { 219 key: 'min_flick_speed_squared', 220 label: 'Minimum Flick Speed Squared', 221 units: '(pixels/sec.)<sup>2</sup>' 222 }, 223 { 224 key: 'min_pinch_update_distance_in_pixels', 225 label: 'Minimum Pinch Update Distance', 226 units: 'pixels' 227 }, 228 { 229 key: 'min_rail_break_velocity', 230 label: 'Minimum Rail-Break Velocity', 231 units: 'pixels/sec.' 232 }, 233 { 234 key: 'min_scroll_delta_squared', 235 label: 'Minimum Scroll Delta Squared', 236 units: '' 237 }, 238 { 239 key: 'scroll_prediction_seconds', 240 label: 'Scroll prediction interval<br>' + 241 '(Enable scroll prediction in ' + 242 '<a href="chrome://flags">chrome://flags</a>)', 243 units: 'seconds', 244 step: 0.01 245 }, 246 { 247 key: 'min_swipe_speed', 248 label: 'Minimum Swipe Speed', 249 units: 'pixels/sec.' 250 }, 251 { 252 key: 'min_touch_down_duration_in_seconds_for_click', 253 label: 'Minimum Touch-Down Duration for Click', 254 units: 'seconds', 255 step: 0.01 256 }, 257 { 258 key: 'points_buffered_for_velocity', 259 label: 'Points Buffered for Velocity', 260 units: '', 261 step: 1 262 }, 263 { 264 key: 'rail_break_proportion', 265 label: 'Rail-Break Proportion', 266 units: '%' 267 }, 268 { 269 key: 'rail_start_proportion', 270 label: 'Rail-Start Proportion', 271 units: '%' 272 }, 273 { 274 key: 'fling_acceleration_curve_coefficient_0', 275 label: 'Touchscreen Fling Acceleration', 276 units: 'x<sup>3</sup>', 277 min: '-1' 278 }, 279 { 280 key: 'fling_acceleration_curve_coefficient_1', 281 label: '+', 282 units: 'x<sup>2</sup>', 283 min: '-1' 284 }, 285 { 286 key: 'fling_acceleration_curve_coefficient_2', 287 label: '+', 288 units: 'x<sup>1</sup>', 289 min: '-1' 290 }, 291 { 292 key: 'fling_acceleration_curve_coefficient_3', 293 label: '+', 294 units: 'x<sup>0</sup>', 295 min: '-1' 296 }, 297 { 298 key: 'fling_velocity_cap', 299 label: 'Touchscreen Fling Velocity Cap', 300 units: 'pixels / second' 301 }, 302 { 303 key: 'tab_scrub_activation_delay_in_ms', 304 label: 'Tab scrub auto activation delay, (-1 for never)', 305 units: 'milliseconds' 306 } 307 ]; 308 309 return new GeneralConfig(GESTURE_TITLE, GESTURE_PREFIX, GESTURE_FIELDS); 310 } 311 312 /** 313 * Returns a GeneralConfig for configuring overscroll.* preferences. 314 * @return {object} A GeneralConfig object. 315 */ 316 function OverscrollConfig() { 317 /** @const */ var OVERSCROLL_TITLE = 'Overscroll Configuration'; 318 319 /** @const */ var OVERSCROLL_PREFIX = 'overscroll.'; 320 321 var OVERSCROLL_FIELDS = [ 322 { 323 key: 'horizontal_threshold_complete', 324 label: 'Complete when overscrolled (horizontal)', 325 units: '%' 326 }, 327 { 328 key: 'vertical_threshold_complete', 329 label: 'Complete when overscrolled (vertical)', 330 units: '%' 331 }, 332 { 333 key: 'minimum_threshold_start', 334 label: 'Start overscroll gesture (horizontal)', 335 units: 'pixels' 336 }, 337 { 338 key: 'vertical_threshold_start', 339 label: 'Start overscroll gesture (vertical)', 340 units: 'pixels' 341 }, 342 { 343 key: 'horizontal_resist_threshold', 344 label: 'Start resisting overscroll after (horizontal)', 345 units: 'pixels' 346 }, 347 { 348 key: 'vertical_resist_threshold', 349 label: 'Start resisting overscroll after (vertical)', 350 units: 'pixels' 351 }, 352 ]; 353 354 return new GeneralConfig(OVERSCROLL_TITLE, 355 OVERSCROLL_PREFIX, 356 OVERSCROLL_FIELDS); 357 } 358 359 /** 360 * Returns a GeneralConfig for configuring immersive.* preferences for 361 * immersive fullscreen in Ash. 362 * @return {object} A GeneralConfig object. 363 */ 364 function ImmersiveConfig() { 365 /** @const */ var IMMERSIVE_TITLE = 'Immersive Fullscreen Configuration'; 366 367 /** @const */ var IMMERSIVE_PREFIX = 'immersive_mode.'; 368 369 var IMMERSIVE_FIELDS = [ 370 { 371 key: 'reveal_delay_ms', 372 label: 'Top-of-screen reveal delay', 373 units: 'milliseconds' 374 }, 375 { 376 key: 'reveal_x_threshold_pixels', 377 label: 'Top-of-screen mouse x threshold', 378 units: 'pixels' 379 }, 380 ]; 381 382 return new GeneralConfig(IMMERSIVE_TITLE, 383 IMMERSIVE_PREFIX, 384 IMMERSIVE_FIELDS); 385 } 386 387 /** 388 * Returns a GeneralConfig for configuring flingcurve.* preferences. 389 * @return {object} A GeneralConfig object. 390 */ 391 function FlingConfig() { 392 /** @const */ var FLING_TITLE = 'Fling Configuration'; 393 394 /** @const */ var FLING_PREFIX = 'flingcurve.'; 395 396 var FLING_FIELDS = [ 397 { 398 key: 'touchscreen_alpha', 399 label: 'Touchscreen fling deacceleration coefficients', 400 units: 'alpha', 401 min: '-inf' 402 }, 403 { 404 key: 'touchscreen_beta', 405 label: '', 406 units: 'beta', 407 min: '-inf' 408 }, 409 { 410 key: 'touchscreen_gamma', 411 label: '', 412 units: 'gamma', 413 min: '-inf' 414 }, 415 { 416 key: 'touchpad_alpha', 417 label: 'Touchpad fling deacceleration coefficients', 418 units: 'alpha', 419 min: '-inf' 420 }, 421 { 422 key: 'touchpad_beta', 423 label: '', 424 units: 'beta', 425 min: '-inf' 426 }, 427 { 428 key: 'touchpad_gamma', 429 label: '', 430 units: 'gamma', 431 min: '-inf' 432 }, 433 ]; 434 435 return new GeneralConfig(FLING_TITLE, FLING_PREFIX, FLING_FIELDS); 436 } 437 438 /** 439 * WebUI instance for configuring preference values related to gesture input. 440 */ 441 window.gesture_config = { 442 /** 443 * Build and initialize the gesture configuration form. 444 */ 445 initialize: function() { 446 var g = GestureConfig(); 447 g.buildAll(); 448 449 var o = OverscrollConfig(); 450 o.buildAll(); 451 452 var f = FlingConfig(); 453 f.buildAll(); 454 455 var i = ImmersiveConfig(); 456 i.buildAll(); 457 458 $('reset-all-button').onclick = function() { 459 g.onReset(); 460 o.onReset(); 461 f.onReset(); 462 i.onReset(); 463 }; 464 }, 465 466 /** 467 * Checks if all gesture preferences are set to default by checking the status 468 * of the reset button associated with each preference. 469 * @return {boolean} True if all gesture preferences are set to default. 470 */ 471 areAllPrefsSetToDefault: function() { 472 var resets = $('gesture-form').querySelectorAll('.row-reset'); 473 for (var i = 0; i < resets.length; i++) { 474 if (!resets[i].disabled) 475 return false; 476 } 477 return true; 478 }, 479 480 /** 481 * Updates the status and label of a preference reset button. 482 * @param {HTMLInputElement} resetButton Reset button for the preference. 483 * @param {boolean} isDefault Whether the preference is set to the default 484 * value. 485 */ 486 updateResetButton: function(resetButton, isDefault) { 487 /** @const */ var TITLE_DEFAULT = 'Default'; 488 489 /** @const */ var TITLE_NOT_DEFAULT = 'Reset'; 490 491 resetButton.innerHTML = isDefault ? TITLE_DEFAULT : TITLE_NOT_DEFAULT; 492 resetButton.disabled = isDefault; 493 }, 494 495 /** 496 * Updates the status and label of "Reset All" button. 497 * @param {boolean} isDefault Whether all preference are set to their default 498 * values. 499 */ 500 updateResetAllButton: function(isDefault) { 501 /** @const */ var TITLE_DEFAULT = 'Everything is set to default'; 502 503 /** @const */ var TITLE_NOT_DEFAULT = 'Reset All To Default'; 504 505 var button = $('reset-all-button'); 506 button.innerHTML = isDefault ? TITLE_DEFAULT : TITLE_NOT_DEFAULT; 507 button.disabled = isDefault; 508 }, 509 510 /** 511 * Handle callback from call to updatePreferenceValue. 512 * @param {string} prefName The name of the requested preference value. 513 * @param {value} value The current value associated with prefName. 514 * @param {boolean} isDefault Whether the value is the default value. 515 */ 516 updatePreferenceValueResult: function(prefName, value, isDefault) { 517 prefName = prefName.substring(prefName.indexOf('.') + 1); 518 $(prefName).value = value; 519 this.updateResetButton($(prefName + '-reset'), isDefault); 520 this.updateResetAllButton(this.areAllPrefsSetToDefault()); 521 }, 522 }; 523 524 document.addEventListener('DOMContentLoaded', gesture_config.initialize); 525