Index: chrome/browser/resources/shared/js/cr/ui/tree.js |
=================================================================== |
--- chrome/browser/resources/shared/js/cr/ui/tree.js (revision 177292) |
+++ chrome/browser/resources/shared/js/cr/ui/tree.js (working copy) |
@@ -1,676 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-cr.define('cr.ui', function() { |
- // require cr.ui.define |
- // require cr.ui.limitInputWidth |
- |
- /** |
- * The number of pixels to indent per level. |
- * @type {number} |
- * @const |
- */ |
- var INDENT = 20; |
- |
- /** |
- * Returns the computed style for an element. |
- * @param {!Element} el The element to get the computed style for. |
- * @return {!CSSStyleDeclaration} The computed style. |
- */ |
- function getComputedStyle(el) { |
- return el.ownerDocument.defaultView.getComputedStyle(el); |
- } |
- |
- /** |
- * Helper function that finds the first ancestor tree item. |
- * @param {!Element} el The element to start searching from. |
- * @return {cr.ui.TreeItem} The found tree item or null if not found. |
- */ |
- function findTreeItem(el) { |
- while (el && !(el instanceof TreeItem)) { |
- el = el.parentNode; |
- } |
- return el; |
- } |
- |
- /** |
- * Creates a new tree element. |
- * @param {Object=} opt_propertyBag Optional properties. |
- * @constructor |
- * @extends {HTMLElement} |
- */ |
- var Tree = cr.ui.define('tree'); |
- |
- Tree.prototype = { |
- __proto__: HTMLElement.prototype, |
- |
- /** |
- * Initializes the element. |
- */ |
- decorate: function() { |
- // Make list focusable |
- if (!this.hasAttribute('tabindex')) |
- this.tabIndex = 0; |
- |
- this.addEventListener('click', this.handleClick); |
- this.addEventListener('mousedown', this.handleMouseDown); |
- this.addEventListener('dblclick', this.handleDblClick); |
- this.addEventListener('keydown', this.handleKeyDown); |
- }, |
- |
- /** |
- * Returns the tree item that are children of this tree. |
- */ |
- get items() { |
- return this.children; |
- }, |
- |
- /** |
- * Adds a tree item to the tree. |
- * @param {!cr.ui.TreeItem} treeItem The item to add. |
- */ |
- add: function(treeItem) { |
- this.addAt(treeItem, 0xffffffff); |
- }, |
- |
- /** |
- * Adds a tree item at the given index. |
- * @param {!cr.ui.TreeItem} treeItem The item to add. |
- * @param {number} index The index where we want to add the item. |
- */ |
- addAt: function(treeItem, index) { |
- this.insertBefore(treeItem, this.children[index]); |
- treeItem.setDepth_(this.depth + 1); |
- }, |
- |
- /** |
- * Removes a tree item child. |
- * @param {!cr.ui.TreeItem} treeItem The tree item to remove. |
- */ |
- remove: function(treeItem) { |
- this.removeChild(treeItem); |
- }, |
- |
- /** |
- * The depth of the node. This is 0 for the tree itself. |
- * @type {number} |
- */ |
- get depth() { |
- return 0; |
- }, |
- |
- /** |
- * Handles click events on the tree and forwards the event to the relevant |
- * tree items as necesary. |
- * @param {Event} e The click event object. |
- */ |
- handleClick: function(e) { |
- var treeItem = findTreeItem(e.target); |
- if (treeItem) |
- treeItem.handleClick(e); |
- }, |
- |
- handleMouseDown: function(e) { |
- if (e.button == 2) // right |
- this.handleClick(e); |
- }, |
- |
- /** |
- * Handles double click events on the tree. |
- * @param {Event} e The dblclick event object. |
- */ |
- handleDblClick: function(e) { |
- var treeItem = findTreeItem(e.target); |
- if (treeItem) |
- treeItem.expanded = !treeItem.expanded; |
- }, |
- |
- /** |
- * Handles keydown events on the tree and updates selection and exanding |
- * of tree items. |
- * @param {Event} e The click event object. |
- */ |
- handleKeyDown: function(e) { |
- var itemToSelect; |
- if (e.ctrlKey) |
- return; |
- |
- var item = this.selectedItem; |
- |
- var rtl = getComputedStyle(item).direction == 'rtl'; |
- |
- switch (e.keyIdentifier) { |
- case 'Up': |
- itemToSelect = item ? getPrevious(item) : |
- this.items[this.items.length - 1]; |
- break; |
- case 'Down': |
- itemToSelect = item ? getNext(item) : |
- this.items[0]; |
- break; |
- case 'Left': |
- case 'Right': |
- // Don't let back/forward keyboard shortcuts be used. |
- if (!cr.isMac && e.altKey || cr.isMac && e.metaKey) |
- break; |
- |
- if (e.keyIdentifier == 'Left' && !rtl || |
- e.keyIdentifier == 'Right' && rtl) { |
- if (item.expanded) |
- item.expanded = false; |
- else |
- itemToSelect = findTreeItem(item.parentNode); |
- } else { |
- if (!item.expanded) |
- item.expanded = true; |
- else |
- itemToSelect = item.items[0]; |
- } |
- break; |
- case 'Home': |
- itemToSelect = this.items[0]; |
- break; |
- case 'End': |
- itemToSelect = this.items[this.items.length - 1]; |
- break; |
- } |
- |
- if (itemToSelect) { |
- itemToSelect.selected = true; |
- e.preventDefault(); |
- } |
- }, |
- |
- /** |
- * The selected tree item or null if none. |
- * @type {cr.ui.TreeItem} |
- */ |
- get selectedItem() { |
- return this.selectedItem_ || null; |
- }, |
- set selectedItem(item) { |
- var oldSelectedItem = this.selectedItem_; |
- if (oldSelectedItem != item) { |
- // Set the selectedItem_ before deselecting the old item since we only |
- // want one change when moving between items. |
- this.selectedItem_ = item; |
- |
- if (oldSelectedItem) |
- oldSelectedItem.selected = false; |
- |
- if (item) |
- item.selected = true; |
- |
- cr.dispatchSimpleEvent(this, 'change'); |
- } |
- }, |
- |
- /** |
- * @return {!ClientRect} The rect to use for the context menu. |
- */ |
- getRectForContextMenu: function() { |
- // TODO(arv): Add trait support so we can share more code between trees |
- // and lists. |
- if (this.selectedItem) |
- return this.selectedItem.rowElement.getBoundingClientRect(); |
- return this.getBoundingClientRect(); |
- } |
- }; |
- |
- /** |
- * Determines the visibility of icons next to the treeItem labels. If set to |
- * 'hidden', no space is reserved for icons and no icons are displayed next |
- * to treeItem labels. If set to 'parent', folder icons will be displayed |
- * next to expandable parent nodes. If set to 'all' folder icons will be |
- * displayed next to all nodes. Icons can be set using the treeItem's icon |
- * property. |
- */ |
- cr.defineProperty(Tree, 'iconVisibility', cr.PropertyKind.ATTR); |
- |
- /** |
- * This is used as a blueprint for new tree item elements. |
- * @type {!HTMLElement} |
- */ |
- var treeItemProto = (function() { |
- var treeItem = cr.doc.createElement('div'); |
- treeItem.className = 'tree-item'; |
- treeItem.innerHTML = '<div class=tree-row>' + |
- '<span class=expand-icon></span>' + |
- '<span class=tree-label></span>' + |
- '</div>' + |
- '<div class=tree-children></div>'; |
- treeItem.setAttribute('role', 'treeitem'); |
- return treeItem; |
- })(); |
- |
- /** |
- * Creates a new tree item. |
- * @param {Object=} opt_propertyBag Optional properties. |
- * @constructor |
- * @extends {HTMLElement} |
- */ |
- var TreeItem = cr.ui.define(function() { |
- return treeItemProto.cloneNode(true); |
- }); |
- |
- TreeItem.prototype = { |
- __proto__: HTMLElement.prototype, |
- |
- /** |
- * Initializes the element. |
- */ |
- decorate: function() { |
- |
- }, |
- |
- /** |
- * The tree items children. |
- */ |
- get items() { |
- return this.lastElementChild.children; |
- }, |
- |
- /** |
- * The depth of the tree item. |
- * @type {number} |
- */ |
- depth_: 0, |
- get depth() { |
- return this.depth_; |
- }, |
- |
- /** |
- * Sets the depth. |
- * @param {number} depth The new depth. |
- * @private |
- */ |
- setDepth_: function(depth) { |
- if (depth != this.depth_) { |
- this.rowElement.style.WebkitPaddingStart = Math.max(0, depth - 1) * |
- INDENT + 'px'; |
- this.depth_ = depth; |
- var items = this.items; |
- for (var i = 0, item; item = items[i]; i++) { |
- item.setDepth_(depth + 1); |
- } |
- } |
- }, |
- |
- /** |
- * Adds a tree item as a child. |
- * @param {!cr.ui.TreeItem} child The child to add. |
- */ |
- add: function(child) { |
- this.addAt(child, 0xffffffff); |
- }, |
- |
- /** |
- * Adds a tree item as a child at a given index. |
- * @param {!cr.ui.TreeItem} child The child to add. |
- * @param {number} index The index where to add the child. |
- */ |
- addAt: function(child, index) { |
- this.lastElementChild.insertBefore(child, this.items[index]); |
- if (this.items.length == 1) |
- this.hasChildren = true; |
- child.setDepth_(this.depth + 1); |
- }, |
- |
- /** |
- * Removes a child. |
- * @param {!cr.ui.TreeItem} child The tree item child to remove. |
- */ |
- remove: function(child) { |
- // If we removed the selected item we should become selected. |
- var tree = this.tree; |
- var selectedItem = tree.selectedItem; |
- if (selectedItem && child.contains(selectedItem)) |
- this.selected = true; |
- |
- this.lastElementChild.removeChild(child); |
- if (this.items.length == 0) |
- this.hasChildren = false; |
- }, |
- |
- /** |
- * The parent tree item. |
- * @type {!cr.ui.Tree|cr.ui.TreeItem} |
- */ |
- get parentItem() { |
- var p = this.parentNode; |
- while (p && !(p instanceof TreeItem) && !(p instanceof Tree)) { |
- p = p.parentNode; |
- } |
- return p; |
- }, |
- |
- /** |
- * The tree that the tree item belongs to or null of no added to a tree. |
- * @type {cr.ui.Tree} |
- */ |
- get tree() { |
- var t = this.parentItem; |
- while (t && !(t instanceof Tree)) { |
- t = t.parentItem; |
- } |
- return t; |
- }, |
- |
- /** |
- * Whether the tree item is expanded or not. |
- * @type {boolean} |
- */ |
- get expanded() { |
- return this.hasAttribute('expanded'); |
- }, |
- set expanded(b) { |
- if (this.expanded == b) |
- return; |
- |
- var treeChildren = this.lastElementChild; |
- |
- if (b) { |
- if (this.mayHaveChildren_) { |
- this.setAttribute('expanded', ''); |
- treeChildren.setAttribute('expanded', ''); |
- cr.dispatchSimpleEvent(this, 'expand', true); |
- this.scrollIntoViewIfNeeded(false); |
- } |
- } else { |
- var tree = this.tree; |
- if (tree && !this.selected) { |
- var oldSelected = tree.selectedItem; |
- if (oldSelected && this.contains(oldSelected)) |
- this.selected = true; |
- } |
- this.removeAttribute('expanded'); |
- treeChildren.removeAttribute('expanded'); |
- cr.dispatchSimpleEvent(this, 'collapse', true); |
- } |
- }, |
- |
- /** |
- * Expands all parent items. |
- */ |
- reveal: function() { |
- var pi = this.parentItem; |
- while (pi && !(pi instanceof Tree)) { |
- pi.expanded = true; |
- pi = pi.parentItem; |
- } |
- }, |
- |
- /** |
- * The element representing the row that gets highlighted. |
- * @type {!HTMLElement} |
- */ |
- get rowElement() { |
- return this.firstElementChild; |
- }, |
- |
- /** |
- * The element containing the label text and the icon. |
- * @type {!HTMLElement} |
- */ |
- get labelElement() { |
- return this.firstElementChild.lastElementChild; |
- }, |
- |
- /** |
- * The label text. |
- * @type {string} |
- */ |
- get label() { |
- return this.labelElement.textContent; |
- }, |
- set label(s) { |
- this.labelElement.textContent = s; |
- }, |
- |
- /** |
- * The URL for the icon. |
- * @type {string} |
- */ |
- get icon() { |
- return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1); |
- }, |
- set icon(icon) { |
- return this.labelElement.style.backgroundImage = url(icon); |
- }, |
- |
- /** |
- * Whether the tree item is selected or not. |
- * @type {boolean} |
- */ |
- get selected() { |
- return this.hasAttribute('selected'); |
- }, |
- set selected(b) { |
- if (this.selected == b) |
- return; |
- var rowItem = this.firstElementChild; |
- var tree = this.tree; |
- if (b) { |
- this.setAttribute('selected', ''); |
- rowItem.setAttribute('selected', ''); |
- this.reveal(); |
- this.labelElement.scrollIntoViewIfNeeded(false); |
- if (tree) |
- tree.selectedItem = this; |
- } else { |
- this.removeAttribute('selected'); |
- rowItem.removeAttribute('selected'); |
- if (tree && tree.selectedItem == this) |
- tree.selectedItem = null; |
- } |
- }, |
- |
- /** |
- * Whether the tree item has children. |
- * @type {boolean} |
- */ |
- get mayHaveChildren_() { |
- return this.hasAttribute('may-have-children'); |
- }, |
- set mayHaveChildren_(b) { |
- var rowItem = this.firstElementChild; |
- if (b) { |
- this.setAttribute('may-have-children', ''); |
- rowItem.setAttribute('may-have-children', ''); |
- } else { |
- this.removeAttribute('may-have-children'); |
- rowItem.removeAttribute('may-have-children'); |
- } |
- }, |
- |
- /** |
- * Whether the tree item has children. |
- * @type {boolean} |
- */ |
- get hasChildren() { |
- return !!this.items[0]; |
- }, |
- |
- /** |
- * Whether the tree item has children. |
- * @type {boolean} |
- */ |
- set hasChildren(b) { |
- var rowItem = this.firstElementChild; |
- this.setAttribute('has-children', b); |
- rowItem.setAttribute('has-children', b); |
- if (b) |
- this.mayHaveChildren_ = true; |
- }, |
- |
- /** |
- * Called when the user clicks on a tree item. This is forwarded from the |
- * cr.ui.Tree. |
- * @param {Event} e The click event. |
- */ |
- handleClick: function(e) { |
- if (e.target.className == 'expand-icon') |
- this.expanded = !this.expanded; |
- else |
- this.selected = true; |
- }, |
- |
- /** |
- * Makes the tree item user editable. If the user renamed the item a |
- * bubbling {@code rename} event is fired. |
- * @type {boolean} |
- */ |
- set editing(editing) { |
- var oldEditing = this.editing; |
- if (editing == oldEditing) |
- return; |
- |
- var self = this; |
- var labelEl = this.labelElement; |
- var text = this.label; |
- var input; |
- |
- // Handles enter and escape which trigger reset and commit respectively. |
- function handleKeydown(e) { |
- // Make sure that the tree does not handle the key. |
- e.stopPropagation(); |
- |
- // Calling tree.focus blurs the input which will make the tree item |
- // non editable. |
- switch (e.keyIdentifier) { |
- case 'U+001B': // Esc |
- input.value = text; |
- // fall through |
- case 'Enter': |
- self.tree.focus(); |
- } |
- } |
- |
- function stopPropagation(e) { |
- e.stopPropagation(); |
- } |
- |
- if (editing) { |
- this.selected = true; |
- this.setAttribute('editing', ''); |
- this.draggable = false; |
- |
- // We create an input[type=text] and copy over the label value. When |
- // the input loses focus we set editing to false again. |
- input = this.ownerDocument.createElement('input'); |
- input.value = text; |
- if (labelEl.firstChild) |
- labelEl.replaceChild(input, labelEl.firstChild); |
- else |
- labelEl.appendChild(input); |
- |
- input.addEventListener('keydown', handleKeydown); |
- input.addEventListener('blur', (function() { |
- this.editing = false; |
- }).bind(this)); |
- |
- // Make sure that double clicks do not expand and collapse the tree |
- // item. |
- var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick']; |
- eventsToStop.forEach(function(type) { |
- input.addEventListener(type, stopPropagation); |
- }); |
- |
- // Wait for the input element to recieve focus before sizing it. |
- var rowElement = this.rowElement; |
- function onFocus() { |
- input.removeEventListener('focus', onFocus); |
- // 20 = the padding and border of the tree-row |
- cr.ui.limitInputWidth(input, rowElement, 100); |
- } |
- input.addEventListener('focus', onFocus); |
- input.focus(); |
- input.select(); |
- |
- this.oldLabel_ = text; |
- } else { |
- this.removeAttribute('editing'); |
- this.draggable = true; |
- input = labelEl.firstChild; |
- var value = input.value; |
- if (/^\s*$/.test(value)) { |
- labelEl.textContent = this.oldLabel_; |
- } else { |
- labelEl.textContent = value; |
- if (value != this.oldLabel_) { |
- cr.dispatchSimpleEvent(this, 'rename', true); |
- } |
- } |
- delete this.oldLabel_; |
- } |
- }, |
- |
- get editing() { |
- return this.hasAttribute('editing'); |
- } |
- }; |
- |
- /** |
- * Helper function that returns the next visible tree item. |
- * @param {cr.ui.TreeItem} item The tree item. |
- * @return {cr.ui.TreeItem} The found item or null. |
- */ |
- function getNext(item) { |
- if (item.expanded) { |
- var firstChild = item.items[0]; |
- if (firstChild) { |
- return firstChild; |
- } |
- } |
- |
- return getNextHelper(item); |
- } |
- |
- /** |
- * Another helper function that returns the next visible tree item. |
- * @param {cr.ui.TreeItem} item The tree item. |
- * @return {cr.ui.TreeItem} The found item or null. |
- */ |
- function getNextHelper(item) { |
- if (!item) |
- return null; |
- |
- var nextSibling = item.nextElementSibling; |
- if (nextSibling) { |
- return nextSibling; |
- } |
- return getNextHelper(item.parentItem); |
- } |
- |
- /** |
- * Helper function that returns the previous visible tree item. |
- * @param {cr.ui.TreeItem} item The tree item. |
- * @return {cr.ui.TreeItem} The found item or null. |
- */ |
- function getPrevious(item) { |
- var previousSibling = item.previousElementSibling; |
- return previousSibling ? getLastHelper(previousSibling) : item.parentItem; |
- } |
- |
- /** |
- * Helper function that returns the last visible tree item in the subtree. |
- * @param {cr.ui.TreeItem} item The item to find the last visible item for. |
- * @return {cr.ui.TreeItem} The found item or null. |
- */ |
- function getLastHelper(item) { |
- if (!item) |
- return null; |
- if (item.expanded && item.hasChildren) { |
- var lastChild = item.items[item.items.length - 1]; |
- return getLastHelper(lastChild); |
- } |
- return item; |
- } |
- |
- // Export |
- return { |
- Tree: Tree, |
- TreeItem: TreeItem |
- }; |
-}); |