OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // How long to wait to open submenu when mouse hovers. | 5 // How long to wait to open submenu when mouse hovers. |
6 var SUBMENU_OPEN_DELAY_MS = 200; | 6 var SUBMENU_OPEN_DELAY_MS = 200; |
7 // How long to wait to close submenu when mouse left. | 7 // How long to wait to close submenu when mouse left. |
8 var SUBMENU_CLOSE_DELAY_MS = 500; | 8 var SUBMENU_CLOSE_DELAY_MS = 500; |
9 // Scroll repeat interval. | 9 // Scroll repeat interval. |
10 var SCROLL_INTERVAL_MS = 20; | 10 var SCROLL_INTERVAL_MS = 20; |
(...skipping 15 matching lines...) Expand all Loading... |
26 function sendActivate(index, mode) { | 26 function sendActivate(index, mode) { |
27 chrome.send('activate', [String(index), mode]); | 27 chrome.send('activate', [String(index), mode]); |
28 } | 28 } |
29 | 29 |
30 /** | 30 /** |
31 * MenuItem class. | 31 * MenuItem class. |
32 */ | 32 */ |
33 var MenuItem = cr.ui.define('div'); | 33 var MenuItem = cr.ui.define('div'); |
34 | 34 |
35 MenuItem.prototype = { | 35 MenuItem.prototype = { |
36 __proto__ : HTMLDivElement.prototype, | 36 __proto__: HTMLDivElement.prototype, |
37 | 37 |
38 /** | 38 /** |
39 * Decorates the menu item element. | 39 * Decorates the menu item element. |
40 */ | 40 */ |
41 decorate: function() { | 41 decorate: function() { |
42 this.className = 'menu-item'; | 42 this.className = 'menu-item'; |
43 }, | 43 }, |
44 | 44 |
45 /** | 45 /** |
46 * Initialize the MenuItem. | 46 * Initialize the MenuItem. |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 */ | 183 */ |
184 var Menu = cr.ui.define('div'); | 184 var Menu = cr.ui.define('div'); |
185 | 185 |
186 Menu.prototype = { | 186 Menu.prototype = { |
187 __proto__: HTMLDivElement.prototype, | 187 __proto__: HTMLDivElement.prototype, |
188 | 188 |
189 /** | 189 /** |
190 * Configuration object. | 190 * Configuration object. |
191 * @type {Object} | 191 * @type {Object} |
192 */ | 192 */ |
193 config_ : null, | 193 config_: null, |
194 | 194 |
195 /** | 195 /** |
196 * Currently selected menu item. | 196 * Currently selected menu item. |
197 * @type {MenuItem} | 197 * @type {MenuItem} |
198 */ | 198 */ |
199 current_ : null, | 199 current_: null, |
200 | 200 |
201 /** | 201 /** |
202 * Timers for opening/closing submenu. | 202 * Timers for opening/closing submenu. |
203 * @type {number} | 203 * @type {number} |
204 */ | 204 */ |
205 openSubmenuTimer_ : 0, | 205 openSubmenuTimer_: 0, |
206 closeSubmenuTimer_ : 0, | 206 closeSubmenuTimer_: 0, |
207 | 207 |
208 /** | 208 /** |
209 * Auto scroll timer. | 209 * Auto scroll timer. |
210 * @type {number} | 210 * @type {number} |
211 */ | 211 */ |
212 scrollTimer_ : 0, | 212 scrollTimer_: 0, |
213 | 213 |
214 /** | 214 /** |
215 * Pointer to a submenu currently shown, if any. | 215 * Pointer to a submenu currently shown, if any. |
216 * @type {MenuItem} | 216 * @type {MenuItem} |
217 */ | 217 */ |
218 submenuShown_ : null, | 218 submenuShown_: null, |
219 | 219 |
220 /** | 220 /** |
221 * True if this menu is root. | 221 * True if this menu is root. |
222 * @type {boolean} | 222 * @type {boolean} |
223 */ | 223 */ |
224 isRoot_ : false, | 224 isRoot_: false, |
225 | |
226 /** | |
227 * Scrollable Viewport. | |
228 * @type {HTMLElement} | |
229 */ | |
230 viewpotr_ : null, | |
231 | 225 |
232 /** | 226 /** |
233 * Total hight of scroll buttons. Used to adjust the height of | 227 * Total hight of scroll buttons. Used to adjust the height of |
234 * viewport in order to show scroll bottons without scrollbar. | 228 * viewport in order to show scroll bottons without scrollbar. |
235 * @type {number} | 229 * @type {number} |
236 */ | 230 */ |
237 buttonHeight_ : 0, | 231 buttonHeight_: 0, |
238 | 232 |
239 /** | 233 /** |
240 * True to enable scroll button. | 234 * True to enable scroll button. |
241 * @type {boolean} | 235 * @type {boolean} |
242 */ | 236 */ |
243 scrollEnabled : false, | 237 scrollEnabled: false, |
244 | 238 |
245 /** | 239 /** |
246 * Decorates the menu element. | 240 * Decorates the menu element. |
247 */ | 241 */ |
248 decorate: function() { | 242 decorate: function() { |
249 this.id = 'viewport'; | 243 this.id = 'viewport'; |
250 }, | 244 }, |
251 | 245 |
252 /** | 246 /** |
253 * Initialize the menu. | 247 * Initialize the menu. |
254 * @param {Object} config Configuration parameters in JSON format. | 248 * @param {Object} config Configuration parameters in JSON format. |
255 * See chromeos/views/native_menu_webui.cc for details. | 249 * See chromeos/views/native_menu_webui.cc for details. |
256 */ | 250 */ |
257 init: function(config) { | 251 init: function(config) { |
258 // List of menu items | 252 // List of menu items |
259 this.items_ = []; | 253 this.items_ = []; |
260 // Map from mnemonic character to item to activate | 254 // Map from mnemonic character to item to activate |
261 this.mnemonics_ = {}; | 255 this.mnemonics_ = {}; |
262 | 256 |
263 this.config_ = config; | 257 this.config_ = config; |
264 this.addEventListener('mouseout', this.onMouseout_.bind(this)); | 258 this.addEventListener('mouseout', this.onMouseout_.bind(this)); |
265 | 259 |
266 document.addEventListener('keydown', this.onKeydown_.bind(this)); | 260 document.addEventListener('keydown', this.onKeydown_.bind(this)); |
267 document.addEventListener('keypress', this.onKeypress_.bind(this)); | 261 document.addEventListener('keypress', this.onKeypress_.bind(this)); |
268 document.addEventListener('mousewheel', this.onMouseWheel_.bind(this)); | 262 document.addEventListener('mousewheel', this.onMouseWheel_.bind(this)); |
269 window.addEventListener('resize', this.onResize_.bind(this)); | 263 window.addEventListener('resize', this.onResize_.bind(this)); |
270 | 264 |
271 // Setup scroll events. | 265 // Setup scroll events. |
272 var up = document.getElementById('scroll-up'); | 266 var up = $('scroll-up'); |
273 var down = document.getElementById('scroll-down'); | 267 var down = $('scroll-down'); |
274 up.addEventListener('mouseout', this.stopScroll_.bind(this)); | 268 up.addEventListener('mouseout', this.stopScroll_.bind(this)); |
275 down.addEventListener('mouseout', this.stopScroll_.bind(this)); | 269 down.addEventListener('mouseout', this.stopScroll_.bind(this)); |
276 var menu = this; | 270 var menu = this; |
277 up.addEventListener('mouseover', | 271 up.addEventListener('mouseover', |
278 function() { | 272 function() { |
279 menu.autoScroll_(-SCROLL_TICK_PX); | 273 menu.autoScroll_(-SCROLL_TICK_PX); |
280 }); | 274 }); |
281 down.addEventListener('mouseover', | 275 down.addEventListener('mouseover', |
282 function() { | 276 function() { |
283 menu.autoScroll_(SCROLL_TICK_PX); | 277 menu.autoScroll_(SCROLL_TICK_PX); |
(...skipping 29 matching lines...) Expand all Loading... |
313 targetDiv.appendChild(document.createElement('span')); | 307 targetDiv.appendChild(document.createElement('span')); |
314 targetDiv.appendChild(document.createTextNode(mnemonic[3])); | 308 targetDiv.appendChild(document.createTextNode(mnemonic[3])); |
315 targetDiv.childNodes[1].className = 'mnemonic'; | 309 targetDiv.childNodes[1].className = 'mnemonic'; |
316 targetDiv.childNodes[1].textContent = mnemonic[2]; | 310 targetDiv.childNodes[1].textContent = mnemonic[2]; |
317 } else { | 311 } else { |
318 targetDiv.textContent = mnemonic.splice(1, 3).join(''); | 312 targetDiv.textContent = mnemonic.splice(1, 3).join(''); |
319 } | 313 } |
320 }, | 314 }, |
321 | 315 |
322 /** | 316 /** |
323 * Returns the index of the {@code item}. | 317 * @return {number} The index of the {@code item}. |
324 */ | 318 */ |
325 getMenuItemIndexOf: function(item) { | 319 getMenuItemIndexOf: function(item) { |
326 return this.items_.indexOf(item); | 320 return this.items_.indexOf(item); |
327 }, | 321 }, |
328 | 322 |
329 /** | 323 /** |
330 * A template method to create an item object. It can be a subclass | 324 * A template method to create an item object. It can be a subclass |
331 * of MenuItem, or any HTMLElement that implements {@code init}, | 325 * of MenuItem, or any HTMLElement that implements {@code init}, |
332 * {@code activate} methods as well as {@code selected} attribute. | 326 * {@code activate} methods as well as {@code selected} attribute. |
333 * @param {Object} attrs The menu item's properties passed from C++. | 327 * @param {Object} attrs The menu item's properties passed from C++. |
| 328 * @return {MenuItem} The created menu item. |
334 */ | 329 */ |
335 createMenuItem: function(attrs) { | 330 createMenuItem: function(attrs) { |
336 return new MenuItem(); | 331 return new MenuItem(); |
337 }, | 332 }, |
338 | 333 |
339 /** | 334 /** |
340 * Update and display the new model. | 335 * Update and display the new model. |
341 */ | 336 */ |
342 updateModel: function(model) { | 337 updateModel: function(model) { |
343 this.isRoot = model.isRoot; | 338 this.isRoot = model.isRoot; |
(...skipping 11 matching lines...) Expand all Loading... |
355 this.onResize_(); | 350 this.onResize_(); |
356 }, | 351 }, |
357 | 352 |
358 /** | 353 /** |
359 * Highlights the currently selected item, or | 354 * Highlights the currently selected item, or |
360 * select the 1st selectable item if none is selected. | 355 * select the 1st selectable item if none is selected. |
361 */ | 356 */ |
362 showSelection: function() { | 357 showSelection: function() { |
363 if (this.current_) { | 358 if (this.current_) { |
364 this.current_.selected = true; | 359 this.current_.selected = true; |
365 } else { | 360 } else { |
366 this.findNextEnabled_(1).selected = true; | 361 this.findNextEnabled_(1).selected = true; |
367 } | 362 } |
368 }, | 363 }, |
369 | 364 |
370 /** | 365 /** |
371 * Add event handlers for the item. | 366 * Add event handlers for the item. |
372 */ | 367 */ |
373 addHandlers: function(item, target) { | 368 addHandlers: function(item, target) { |
374 var menu = this; | 369 var menu = this; |
375 target.addEventListener('mouseover', function(event) { | 370 target.addEventListener('mouseover', function(event) { |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
460 this.classList.add('mnemonic-enabled'); | 455 this.classList.add('mnemonic-enabled'); |
461 this.findNextEnabled_(-1).selected = true; | 456 this.findNextEnabled_(-1).selected = true; |
462 break; | 457 break; |
463 case 'Down': | 458 case 'Down': |
464 this.classList.add('mnemonic-enabled'); | 459 this.classList.add('mnemonic-enabled'); |
465 this.findNextEnabled_(1).selected = true; | 460 this.findNextEnabled_(1).selected = true; |
466 break; | 461 break; |
467 case 'U+0009': // tab | 462 case 'U+0009': // tab |
468 break; | 463 break; |
469 case 'U+001B': // escape | 464 case 'U+001B': // escape |
470 chrome.send('close_all', []); | 465 chrome.send('close_all'); |
471 break; | 466 break; |
472 case 'Enter': | 467 case 'Enter': |
473 case 'U+0020': // space | 468 case 'U+0020': // space |
474 if (this.current_) { | 469 if (this.current_) { |
475 this.current_.activate(); | 470 this.current_.activate(); |
476 } | 471 } |
477 break; | 472 break; |
478 } | 473 } |
479 }, | 474 }, |
480 | 475 |
(...skipping 25 matching lines...) Expand all Loading... |
506 } | 501 } |
507 }, | 502 }, |
508 | 503 |
509 onMouseout_: function(event) { | 504 onMouseout_: function(event) { |
510 if (this.current_) { | 505 if (this.current_) { |
511 this.current_.selected = false; | 506 this.current_.selected = false; |
512 } | 507 } |
513 }, | 508 }, |
514 | 509 |
515 onResize_: function() { | 510 onResize_: function() { |
516 var up = document.getElementById('scroll-up'); | 511 var up = $('scroll-up'); |
517 var down = document.getElementById('scroll-down'); | 512 var down = $('scroll-down'); |
518 // this needs to be < 2 as empty page has height of 1. | 513 // this needs to be < 2 as empty page has height of 1. |
519 if (window.innerHeight < 2) { | 514 if (window.innerHeight < 2) { |
520 // menu window is not visible yet. just hide buttons. | 515 // menu window is not visible yet. just hide buttons. |
521 up.classList.add('hidden'); | 516 up.classList.add('hidden'); |
522 down.classList.add('hidden'); | 517 down.classList.add('hidden'); |
523 return; | 518 return; |
524 } | 519 } |
525 // Do not use screen width to determin if we need scroll buttons | 520 // Do not use screen width to determin if we need scroll buttons |
526 // as the max renderer hight can be shorter than actual screen size. | 521 // as the max renderer hight can be shorter than actual screen size. |
527 // TODO(oshima): Fix this when we implement transparent renderer. | 522 // TODO(oshima): Fix this when we implement transparent renderer. |
(...skipping 14 matching lines...) Expand all Loading... |
542 }, | 537 }, |
543 | 538 |
544 /** | 539 /** |
545 * Closes the submenu. | 540 * Closes the submenu. |
546 * a submenu. | 541 * a submenu. |
547 * @private | 542 * @private |
548 */ | 543 */ |
549 closeSubmenu_: function(item) { | 544 closeSubmenu_: function(item) { |
550 this.submenuShown_ = null; | 545 this.submenuShown_ = null; |
551 this.cancelSubmenuTimer_(); | 546 this.cancelSubmenuTimer_(); |
552 chrome.send('close_submenu', []); | 547 chrome.send('close_submenu'); |
553 }, | 548 }, |
554 | 549 |
555 /** | 550 /** |
556 * Move the selection to parent menu if the current menu is | 551 * Move the selection to parent menu if the current menu is |
557 * a submenu. | 552 * a submenu. |
558 * @private | 553 * @private |
559 */ | 554 */ |
560 moveToParent_: function() { | 555 moveToParent_: function() { |
561 if (!this.isRoot) { | 556 if (!this.isRoot) { |
562 if (this.current_) { | 557 if (this.current_) { |
563 this.current_.selected = false; | 558 this.current_.selected = false; |
564 } | 559 } |
565 chrome.send('move_to_parent', []); | 560 chrome.send('move_to_parent'); |
566 } | 561 } |
567 }, | 562 }, |
568 | 563 |
569 /** | 564 /** |
570 * Move the selection to submenu if the currently selected | 565 * Move the selection to submenu if the currently selected |
571 * menu is a submenu. | 566 * menu is a submenu. |
572 * @private | 567 * @private |
573 */ | 568 */ |
574 moveToSubmenu_: function () { | 569 moveToSubmenu_: function() { |
575 var current = this.current_; | 570 var current = this.current_; |
576 if (current && current.attrs.type == 'submenu') { | 571 if (current && current.attrs.type == 'submenu') { |
577 this.openSubmenu(current); | 572 this.openSubmenu(current); |
578 chrome.send('move_to_submenu', []); | 573 chrome.send('move_to_submenu'); |
579 } | 574 } |
580 }, | 575 }, |
581 | 576 |
582 /** | 577 /** |
583 * Find a next selectable item. If nothing is selected, the 1st | 578 * Finds the next selectable item. If nothing is selected, the first |
584 * selectable item will be chosen. Returns null if nothing is | 579 * selectable item will be chosen. Returns null if nothing is selectable. |
585 * selectable. | |
586 * @param {number} incr Specifies the direction to search, 1 to | 580 * @param {number} incr Specifies the direction to search, 1 to |
587 * downwards and -1 for upwards. | 581 * downwards and -1 for upwards. |
588 * @private | 582 * @private |
| 583 * @return {MenuItem} The next selectable item. |
589 */ | 584 */ |
590 findNextEnabled_: function(incr) { | 585 findNextEnabled_: function(incr) { |
591 var len = this.items_.length; | 586 var len = this.items_.length; |
592 var index; | 587 var index; |
593 if (this.current_) { | 588 if (this.current_) { |
594 index = this.getMenuItemIndexOf(this.current_); | 589 index = this.getMenuItemIndexOf(this.current_); |
595 } else { | 590 } else { |
596 index = incr > 0 ? -1 : len; | 591 index = incr > 0 ? -1 : len; |
597 } | 592 } |
598 for (var i = 0; i < len; i++) { | 593 for (var i = 0; i < len; i++) { |
(...skipping 30 matching lines...) Expand all Loading... |
629 function() { | 624 function() { |
630 menu.autoScroll_(tick); | 625 menu.autoScroll_(tick); |
631 }, | 626 }, |
632 SCROLL_INTERVAL_MS); | 627 SCROLL_INTERVAL_MS); |
633 }, | 628 }, |
634 | 629 |
635 /** | 630 /** |
636 * Stops auto scroll. | 631 * Stops auto scroll. |
637 * @private | 632 * @private |
638 */ | 633 */ |
639 stopScroll_: function () { | 634 stopScroll_: function() { |
640 clearTimeout(this.scrollTimer_); | 635 clearTimeout(this.scrollTimer_); |
641 this.scrollTimer_ = 0; | 636 this.scrollTimer_ = 0; |
642 }, | 637 }, |
643 | 638 |
644 /** | 639 /** |
645 * Scrolls the viewport to make the selected item visible. | 640 * Scrolls the viewport to make the selected item visible. |
646 * @private | 641 * @private |
647 */ | 642 */ |
648 makeSelectedItemVisible_: function(){ | 643 makeSelectedItemVisible_: function() { |
649 this.current_.scrollIntoViewIfNeeded(false); | 644 this.current_.scrollIntoViewIfNeeded(false); |
650 }, | 645 }, |
651 }; | 646 }; |
652 | 647 |
653 /** | 648 /** |
654 * functions to be called from C++. | 649 * functions to be called from C++. |
| 650 * @param {Object} config The viewport configuration. |
655 */ | 651 */ |
656 function init(config) { | 652 function init(config) { |
657 document.getElementById('viewport').init(config); | 653 $('viewport').init(config); |
658 } | 654 } |
659 | 655 |
660 function selectItem() { | 656 function selectItem() { |
661 document.getElementById('viewport').showSelection(); | 657 $('viewport').showSelection(); |
662 } | 658 } |
663 | 659 |
664 function updateModel(model) { | 660 function updateModel(model) { |
665 document.getElementById('viewport').updateModel(model); | 661 $('viewport').updateModel(model); |
666 } | 662 } |
667 | 663 |
668 function modelUpdated() { | 664 function modelUpdated() { |
669 chrome.send('model_updated', []); | 665 chrome.send('model_updated'); |
670 } | 666 } |
671 | 667 |
672 function enableScroll(enabled) { | 668 function enableScroll(enabled) { |
673 document.getElementById('viewport').scrollEnabled = enabled; | 669 $('viewport').scrollEnabled = enabled; |
674 } | 670 } |
OLD | NEW |