| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 cr.define('cr.ui', function() { | 5 cr.define('cr.ui', function() { |
| 6 // require cr.ui.define | 6 // require cr.ui.define |
| 7 // require cr.ui.limitInputWidth | 7 // require cr.ui.limitInputWidth |
| 8 | 8 |
| 9 /** | 9 /** |
| 10 * The number of pixels to indent per level. | 10 * The number of pixels to indent per level. |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 this.addEventListener('mousedown', this.handleMouseDown); | 57 this.addEventListener('mousedown', this.handleMouseDown); |
| 58 this.addEventListener('dblclick', this.handleDblClick); | 58 this.addEventListener('dblclick', this.handleDblClick); |
| 59 this.addEventListener('keydown', this.handleKeyDown); | 59 this.addEventListener('keydown', this.handleKeyDown); |
| 60 | 60 |
| 61 this.setAttribute('role', 'group'); | 61 this.setAttribute('role', 'group'); |
| 62 }, | 62 }, |
| 63 | 63 |
| 64 /** | 64 /** |
| 65 * Returns the tree item that are children of this tree. | 65 * Returns the tree item that are children of this tree. |
| 66 */ | 66 */ |
| 67 get items() { return this.children; }, | 67 get items() { |
| 68 return this.children; |
| 69 }, |
| 68 | 70 |
| 69 /** | 71 /** |
| 70 * Adds a tree item to the tree. | 72 * Adds a tree item to the tree. |
| 71 * @param {!cr.ui.TreeItem} treeItem The item to add. | 73 * @param {!cr.ui.TreeItem} treeItem The item to add. |
| 72 */ | 74 */ |
| 73 add: function(treeItem) { this.addAt(treeItem, 0xffffffff); }, | 75 add: function(treeItem) { |
| 76 this.addAt(treeItem, 0xffffffff); |
| 77 }, |
| 74 | 78 |
| 75 /** | 79 /** |
| 76 * Adds a tree item at the given index. | 80 * Adds a tree item at the given index. |
| 77 * @param {!cr.ui.TreeItem} treeItem The item to add. | 81 * @param {!cr.ui.TreeItem} treeItem The item to add. |
| 78 * @param {number} index The index where we want to add the item. | 82 * @param {number} index The index where we want to add the item. |
| 79 */ | 83 */ |
| 80 addAt: function(treeItem, index) { | 84 addAt: function(treeItem, index) { |
| 81 this.insertBefore(treeItem, this.children[index]); | 85 this.insertBefore(treeItem, this.children[index]); |
| 82 treeItem.setDepth_(this.depth + 1); | 86 treeItem.setDepth_(this.depth + 1); |
| 83 }, | 87 }, |
| 84 | 88 |
| 85 /** | 89 /** |
| 86 * Removes a tree item child. | 90 * Removes a tree item child. |
| 87 * | 91 * |
| 88 * TODO(dbeam): this method now conflicts with HTMLElement#remove(), which | 92 * TODO(dbeam): this method now conflicts with HTMLElement#remove(), which |
| 89 * is why the @param is optional. Rename. | 93 * is why the @param is optional. Rename. |
| 90 * | 94 * |
| 91 * @param {!cr.ui.TreeItem=} treeItem The tree item to remove. | 95 * @param {!cr.ui.TreeItem=} treeItem The tree item to remove. |
| 92 */ | 96 */ |
| 93 remove: function(treeItem) { | 97 remove: function(treeItem) { |
| 94 this.removeChild(/** @type {!cr.ui.TreeItem} */ (treeItem)); | 98 this.removeChild(/** @type {!cr.ui.TreeItem} */ (treeItem)); |
| 95 }, | 99 }, |
| 96 | 100 |
| 97 /** | 101 /** |
| 98 * The depth of the node. This is 0 for the tree itself. | 102 * The depth of the node. This is 0 for the tree itself. |
| 99 * @type {number} | 103 * @type {number} |
| 100 */ | 104 */ |
| 101 get depth() { return 0; }, | 105 get depth() { |
| 106 return 0; |
| 107 }, |
| 102 | 108 |
| 103 /** | 109 /** |
| 104 * Handles click events on the tree and forwards the event to the relevant | 110 * Handles click events on the tree and forwards the event to the relevant |
| 105 * tree items as necesary. | 111 * tree items as necesary. |
| 106 * @param {Event} e The click event object. | 112 * @param {Event} e The click event object. |
| 107 */ | 113 */ |
| 108 handleClick: function(e) { | 114 handleClick: function(e) { |
| 109 var treeItem = findTreeItem(/** @type {!Node} */ (e.target)); | 115 var treeItem = findTreeItem(/** @type {!Node} */ (e.target)); |
| 110 if (treeItem) | 116 if (treeItem) |
| 111 treeItem.handleClick(e); | 117 treeItem.handleClick(e); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 if (itemToSelect) { | 185 if (itemToSelect) { |
| 180 itemToSelect.selected = true; | 186 itemToSelect.selected = true; |
| 181 e.preventDefault(); | 187 e.preventDefault(); |
| 182 } | 188 } |
| 183 }, | 189 }, |
| 184 | 190 |
| 185 /** | 191 /** |
| 186 * The selected tree item or null if none. | 192 * The selected tree item or null if none. |
| 187 * @type {cr.ui.TreeItem} | 193 * @type {cr.ui.TreeItem} |
| 188 */ | 194 */ |
| 189 get selectedItem() { return this.selectedItem_ || null; }, | 195 get selectedItem() { |
| 196 return this.selectedItem_ || null; |
| 197 }, |
| 190 set selectedItem(item) { | 198 set selectedItem(item) { |
| 191 var oldSelectedItem = this.selectedItem_; | 199 var oldSelectedItem = this.selectedItem_; |
| 192 if (oldSelectedItem != item) { | 200 if (oldSelectedItem != item) { |
| 193 // Set the selectedItem_ before deselecting the old item since we only | 201 // Set the selectedItem_ before deselecting the old item since we only |
| 194 // want one change when moving between items. | 202 // want one change when moving between items. |
| 195 this.selectedItem_ = item; | 203 this.selectedItem_ = item; |
| 196 | 204 |
| 197 if (oldSelectedItem) | 205 if (oldSelectedItem) |
| 198 oldSelectedItem.selected = false; | 206 oldSelectedItem.selected = false; |
| 199 | 207 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 275 decorate: function() { | 283 decorate: function() { |
| 276 var labelId = | 284 var labelId = |
| 277 'tree-item-label-autogen-id-' + treeItemAutoGeneratedIdCounter; | 285 'tree-item-label-autogen-id-' + treeItemAutoGeneratedIdCounter; |
| 278 this.labelElement.id = labelId; | 286 this.labelElement.id = labelId; |
| 279 this.setAttribute('aria-labelledby', labelId); | 287 this.setAttribute('aria-labelledby', labelId); |
| 280 }, | 288 }, |
| 281 | 289 |
| 282 /** | 290 /** |
| 283 * The tree items children. | 291 * The tree items children. |
| 284 */ | 292 */ |
| 285 get items() { return this.lastElementChild.children; }, | 293 get items() { |
| 294 return this.lastElementChild.children; |
| 295 }, |
| 286 | 296 |
| 287 /** | 297 /** |
| 288 * The depth of the tree item. | 298 * The depth of the tree item. |
| 289 * @type {number} | 299 * @type {number} |
| 290 */ | 300 */ |
| 291 depth_: 0, get depth() { return this.depth_; }, | 301 depth_: 0, |
| 302 get depth() { |
| 303 return this.depth_; |
| 304 }, |
| 292 | 305 |
| 293 /** | 306 /** |
| 294 * Sets the depth. | 307 * Sets the depth. |
| 295 * @param {number} depth The new depth. | 308 * @param {number} depth The new depth. |
| 296 * @private | 309 * @private |
| 297 */ | 310 */ |
| 298 setDepth_: function(depth) { | 311 setDepth_: function(depth) { |
| 299 if (depth != this.depth_) { | 312 if (depth != this.depth_) { |
| 300 this.rowElement.style.WebkitPaddingStart = | 313 this.rowElement.style.WebkitPaddingStart = |
| 301 Math.max(0, depth - 1) * INDENT + 'px'; | 314 Math.max(0, depth - 1) * INDENT + 'px'; |
| 302 this.depth_ = depth; | 315 this.depth_ = depth; |
| 303 var items = this.items; | 316 var items = this.items; |
| 304 for (var i = 0, item; item = items[i]; i++) { | 317 for (var i = 0, item; item = items[i]; i++) { |
| 305 item.setDepth_(depth + 1); | 318 item.setDepth_(depth + 1); |
| 306 } | 319 } |
| 307 } | 320 } |
| 308 }, | 321 }, |
| 309 | 322 |
| 310 /** | 323 /** |
| 311 * Adds a tree item as a child. | 324 * Adds a tree item as a child. |
| 312 * @param {!cr.ui.TreeItem} child The child to add. | 325 * @param {!cr.ui.TreeItem} child The child to add. |
| 313 */ | 326 */ |
| 314 add: function(child) { this.addAt(child, 0xffffffff); }, | 327 add: function(child) { |
| 328 this.addAt(child, 0xffffffff); |
| 329 }, |
| 315 | 330 |
| 316 /** | 331 /** |
| 317 * Adds a tree item as a child at a given index. | 332 * Adds a tree item as a child at a given index. |
| 318 * @param {!cr.ui.TreeItem} child The child to add. | 333 * @param {!cr.ui.TreeItem} child The child to add. |
| 319 * @param {number} index The index where to add the child. | 334 * @param {number} index The index where to add the child. |
| 320 */ | 335 */ |
| 321 addAt: function(child, index) { | 336 addAt: function(child, index) { |
| 322 this.lastElementChild.insertBefore(child, this.items[index]); | 337 this.lastElementChild.insertBefore(child, this.items[index]); |
| 323 if (this.items.length == 1) | 338 if (this.items.length == 1) |
| 324 this.hasChildren = true; | 339 this.hasChildren = true; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 363 while (t && !(t instanceof Tree)) { | 378 while (t && !(t instanceof Tree)) { |
| 364 t = t.parentItem; | 379 t = t.parentItem; |
| 365 } | 380 } |
| 366 return t; | 381 return t; |
| 367 }, | 382 }, |
| 368 | 383 |
| 369 /** | 384 /** |
| 370 * Whether the tree item is expanded or not. | 385 * Whether the tree item is expanded or not. |
| 371 * @type {boolean} | 386 * @type {boolean} |
| 372 */ | 387 */ |
| 373 get expanded() { return this.hasAttribute('expanded'); }, | 388 get expanded() { |
| 389 return this.hasAttribute('expanded'); |
| 390 }, |
| 374 set expanded(b) { | 391 set expanded(b) { |
| 375 if (this.expanded == b) | 392 if (this.expanded == b) |
| 376 return; | 393 return; |
| 377 | 394 |
| 378 var treeChildren = this.lastElementChild; | 395 var treeChildren = this.lastElementChild; |
| 379 | 396 |
| 380 if (b) { | 397 if (b) { |
| 381 if (this.mayHaveChildren_) { | 398 if (this.mayHaveChildren_) { |
| 382 this.setAttribute('expanded', ''); | 399 this.setAttribute('expanded', ''); |
| 383 this.setAttribute('aria-expanded', 'true'); | 400 this.setAttribute('aria-expanded', 'true'); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 410 while (pi && !(pi instanceof Tree)) { | 427 while (pi && !(pi instanceof Tree)) { |
| 411 pi.expanded = true; | 428 pi.expanded = true; |
| 412 pi = pi.parentItem; | 429 pi = pi.parentItem; |
| 413 } | 430 } |
| 414 }, | 431 }, |
| 415 | 432 |
| 416 /** | 433 /** |
| 417 * The element representing the row that gets highlighted. | 434 * The element representing the row that gets highlighted. |
| 418 * @type {!HTMLElement} | 435 * @type {!HTMLElement} |
| 419 */ | 436 */ |
| 420 get rowElement() { return this.firstElementChild; }, | 437 get rowElement() { |
| 438 return this.firstElementChild; |
| 439 }, |
| 421 | 440 |
| 422 /** | 441 /** |
| 423 * The element containing the label text and the icon. | 442 * The element containing the label text and the icon. |
| 424 * @type {!HTMLElement} | 443 * @type {!HTMLElement} |
| 425 */ | 444 */ |
| 426 get labelElement() { return this.firstElementChild.lastElementChild; }, | 445 get labelElement() { |
| 446 return this.firstElementChild.lastElementChild; |
| 447 }, |
| 427 | 448 |
| 428 /** | 449 /** |
| 429 * The label text. | 450 * The label text. |
| 430 * @type {string} | 451 * @type {string} |
| 431 */ | 452 */ |
| 432 get label() { return this.labelElement.textContent; }, | 453 get label() { |
| 433 set label(s) { this.labelElement.textContent = s; }, | 454 return this.labelElement.textContent; |
| 455 }, |
| 456 set label(s) { |
| 457 this.labelElement.textContent = s; |
| 458 }, |
| 434 | 459 |
| 435 /** | 460 /** |
| 436 * The URL for the icon. | 461 * The URL for the icon. |
| 437 * @type {string} | 462 * @type {string} |
| 438 */ | 463 */ |
| 439 get icon() { | 464 get icon() { |
| 440 return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1); | 465 return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1); |
| 441 }, | 466 }, |
| 442 set icon(icon) { | 467 set icon(icon) { |
| 443 return this.labelElement.style.backgroundImage = url(icon); | 468 return this.labelElement.style.backgroundImage = url(icon); |
| 444 }, | 469 }, |
| 445 | 470 |
| 446 /** | 471 /** |
| 447 * Whether the tree item is selected or not. | 472 * Whether the tree item is selected or not. |
| 448 * @type {boolean} | 473 * @type {boolean} |
| 449 */ | 474 */ |
| 450 get selected() { return this.hasAttribute('selected'); }, | 475 get selected() { |
| 476 return this.hasAttribute('selected'); |
| 477 }, |
| 451 set selected(b) { | 478 set selected(b) { |
| 452 if (this.selected == b) | 479 if (this.selected == b) |
| 453 return; | 480 return; |
| 454 var rowItem = this.firstElementChild; | 481 var rowItem = this.firstElementChild; |
| 455 var tree = this.tree; | 482 var tree = this.tree; |
| 456 if (b) { | 483 if (b) { |
| 457 this.setAttribute('selected', ''); | 484 this.setAttribute('selected', ''); |
| 458 rowItem.setAttribute('selected', ''); | 485 rowItem.setAttribute('selected', ''); |
| 459 this.reveal(); | 486 this.reveal(); |
| 460 this.labelElement.scrollIntoViewIfNeeded(false); | 487 this.labelElement.scrollIntoViewIfNeeded(false); |
| 461 if (tree) | 488 if (tree) |
| 462 tree.selectedItem = this; | 489 tree.selectedItem = this; |
| 463 } else { | 490 } else { |
| 464 this.removeAttribute('selected'); | 491 this.removeAttribute('selected'); |
| 465 rowItem.removeAttribute('selected'); | 492 rowItem.removeAttribute('selected'); |
| 466 if (tree && tree.selectedItem == this) | 493 if (tree && tree.selectedItem == this) |
| 467 tree.selectedItem = null; | 494 tree.selectedItem = null; |
| 468 } | 495 } |
| 469 }, | 496 }, |
| 470 | 497 |
| 471 /** | 498 /** |
| 472 * Whether the tree item has children. | 499 * Whether the tree item has children. |
| 473 * @type {boolean} | 500 * @type {boolean} |
| 474 */ | 501 */ |
| 475 get mayHaveChildren_() { return this.hasAttribute('may-have-children'); }, | 502 get mayHaveChildren_() { |
| 503 return this.hasAttribute('may-have-children'); |
| 504 }, |
| 476 set mayHaveChildren_(b) { | 505 set mayHaveChildren_(b) { |
| 477 var rowItem = this.firstElementChild; | 506 var rowItem = this.firstElementChild; |
| 478 if (b) { | 507 if (b) { |
| 479 this.setAttribute('may-have-children', ''); | 508 this.setAttribute('may-have-children', ''); |
| 480 rowItem.setAttribute('may-have-children', ''); | 509 rowItem.setAttribute('may-have-children', ''); |
| 481 } else { | 510 } else { |
| 482 this.removeAttribute('may-have-children'); | 511 this.removeAttribute('may-have-children'); |
| 483 rowItem.removeAttribute('may-have-children'); | 512 rowItem.removeAttribute('may-have-children'); |
| 484 } | 513 } |
| 485 }, | 514 }, |
| 486 | 515 |
| 487 /** | 516 /** |
| 488 * Whether the tree item has children. | 517 * Whether the tree item has children. |
| 489 * @type {boolean} | 518 * @type {boolean} |
| 490 */ | 519 */ |
| 491 get hasChildren() { return !!this.items[0]; }, | 520 get hasChildren() { |
| 521 return !!this.items[0]; |
| 522 }, |
| 492 | 523 |
| 493 /** | 524 /** |
| 494 * Whether the tree item has children. | 525 * Whether the tree item has children. |
| 495 * @type {boolean} | 526 * @type {boolean} |
| 496 */ | 527 */ |
| 497 set hasChildren(b) { | 528 set hasChildren(b) { |
| 498 var rowItem = this.firstElementChild; | 529 var rowItem = this.firstElementChild; |
| 499 this.setAttribute('has-children', b); | 530 this.setAttribute('has-children', b); |
| 500 rowItem.setAttribute('has-children', b); | 531 rowItem.setAttribute('has-children', b); |
| 501 if (b) { | 532 if (b) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 540 // non editable. | 571 // non editable. |
| 541 switch (e.key) { | 572 switch (e.key) { |
| 542 case 'Escape': | 573 case 'Escape': |
| 543 input.value = text; | 574 input.value = text; |
| 544 // fall through | 575 // fall through |
| 545 case 'Enter': | 576 case 'Enter': |
| 546 self.tree.focus(); | 577 self.tree.focus(); |
| 547 } | 578 } |
| 548 } | 579 } |
| 549 | 580 |
| 550 function stopPropagation(e) { e.stopPropagation(); } | 581 function stopPropagation(e) { |
| 582 e.stopPropagation(); |
| 583 } |
| 551 | 584 |
| 552 if (editing) { | 585 if (editing) { |
| 553 this.selected = true; | 586 this.selected = true; |
| 554 this.setAttribute('editing', ''); | 587 this.setAttribute('editing', ''); |
| 555 this.draggable = false; | 588 this.draggable = false; |
| 556 | 589 |
| 557 // We create an input[type=text] and copy over the label value. When | 590 // We create an input[type=text] and copy over the label value. When |
| 558 // the input loses focus we set editing to false again. | 591 // the input loses focus we set editing to false again. |
| 559 input = this.ownerDocument.createElement('input'); | 592 input = this.ownerDocument.createElement('input'); |
| 560 input.value = text; | 593 input.value = text; |
| 561 if (labelEl.firstChild) | 594 if (labelEl.firstChild) |
| 562 labelEl.replaceChild(input, labelEl.firstChild); | 595 labelEl.replaceChild(input, labelEl.firstChild); |
| 563 else | 596 else |
| 564 labelEl.appendChild(input); | 597 labelEl.appendChild(input); |
| 565 | 598 |
| 566 input.addEventListener('keydown', handleKeydown); | 599 input.addEventListener('keydown', handleKeydown); |
| 567 input.addEventListener( | 600 input.addEventListener('blur', (function() { |
| 568 'blur', (function() { this.editing = false; }).bind(this)); | 601 this.editing = false; |
| 602 }).bind(this)); |
| 569 | 603 |
| 570 // Make sure that double clicks do not expand and collapse the tree | 604 // Make sure that double clicks do not expand and collapse the tree |
| 571 // item. | 605 // item. |
| 572 var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick']; | 606 var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick']; |
| 573 eventsToStop.forEach(function(type) { | 607 eventsToStop.forEach(function(type) { |
| 574 input.addEventListener(type, stopPropagation); | 608 input.addEventListener(type, stopPropagation); |
| 575 }); | 609 }); |
| 576 | 610 |
| 577 // Wait for the input element to recieve focus before sizing it. | 611 // Wait for the input element to recieve focus before sizing it. |
| 578 var rowElement = this.rowElement; | 612 var rowElement = this.rowElement; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 596 } else { | 630 } else { |
| 597 labelEl.textContent = value; | 631 labelEl.textContent = value; |
| 598 if (value != this.oldLabel_) { | 632 if (value != this.oldLabel_) { |
| 599 cr.dispatchSimpleEvent(this, 'rename', true); | 633 cr.dispatchSimpleEvent(this, 'rename', true); |
| 600 } | 634 } |
| 601 } | 635 } |
| 602 delete this.oldLabel_; | 636 delete this.oldLabel_; |
| 603 } | 637 } |
| 604 }, | 638 }, |
| 605 | 639 |
| 606 get editing() { return this.hasAttribute('editing'); } | 640 get editing() { |
| 641 return this.hasAttribute('editing'); |
| 642 } |
| 607 }; | 643 }; |
| 608 | 644 |
| 609 /** | 645 /** |
| 610 * Helper function that returns the next visible tree item. | 646 * Helper function that returns the next visible tree item. |
| 611 * @param {cr.ui.TreeItem} item The tree item. | 647 * @param {cr.ui.TreeItem} item The tree item. |
| 612 * @return {cr.ui.TreeItem} The found item or null. | 648 * @return {cr.ui.TreeItem} The found item or null. |
| 613 */ | 649 */ |
| 614 function getNext(item) { | 650 function getNext(item) { |
| 615 if (item.expanded) { | 651 if (item.expanded) { |
| 616 var firstChild = item.items[0]; | 652 var firstChild = item.items[0]; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 660 if (item.expanded && item.hasChildren) { | 696 if (item.expanded && item.hasChildren) { |
| 661 var lastChild = item.items[item.items.length - 1]; | 697 var lastChild = item.items[item.items.length - 1]; |
| 662 return getLastHelper(lastChild); | 698 return getLastHelper(lastChild); |
| 663 } | 699 } |
| 664 return item; | 700 return item; |
| 665 } | 701 } |
| 666 | 702 |
| 667 // Export | 703 // Export |
| 668 return {Tree: Tree, TreeItem: TreeItem}; | 704 return {Tree: Tree, TreeItem: TreeItem}; |
| 669 }); | 705 }); |
| OLD | NEW |