Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4)

Side by Side Diff: resources/bookmark_manager/js/cr/ui/tree.js

Issue 853002: Updating the Chromium reference build for Windows. The continuous... (Closed) Base URL: svn://chrome-svn/chrome/trunk/deps/reference_builds/chrome/
Patch Set: Added the symbol files back. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 cr.define('cr.ui', function() {
6 // require cr.ui.define
7 // require cr.ui.limitInputWidth
8
9 /**
10 * Helper function that finds the first ancestor tree item.
11 * @param {!Element} el The element to start searching from.
12 * @return {cr.ui.TreeItem} The found tree item or null if not found.
13 */
14 function findTreeItem(el) {
15 while (el && !(el instanceof TreeItem)) {
16 el = el.parentNode;
17 }
18 return el;
19 }
20
21 /**
22 * Creates a new tree element.
23 * @param {Object=} opt_propertyBag Optional properties.
24 * @constructor
25 * @extends {HTMLElement}
26 */
27 var Tree = cr.ui.define('tree');
28
29 Tree.prototype = {
30 __proto__: HTMLElement.prototype,
31
32 /**
33 * Initializes the element.
34 */
35 decorate: function() {
36 // Make list focusable
37 if (!this.hasAttribute('tabindex'))
38 this.tabIndex = 0;
39
40 this.addEventListener('click', this.handleClick);
41 this.addEventListener('mousedown', this.handleMouseDown);
42 this.addEventListener('dblclick', this.handleDblClick);
43 this.addEventListener('keydown', this.handleKeyDown);
44 },
45
46 /**
47 * Returns the tree item that are children of this tree.
48 */
49 get items() {
50 return this.children;
51 },
52
53 /**
54 * Adds a tree item to the tree.
55 * @param {!cr.ui.TreeItem} treeItem The item to add.
56 */
57 add: function(treeItem) {
58 this.appendChild(treeItem);
59 },
60
61 /**
62 * Adds a tree item at the given index.
63 * @param {!cr.ui.TreeItem} treeItem The item to add.
64 * @param {number} index The index where we want to add the item.
65 */
66 addAt: function(treeItem, index) {
67 this.insertBefore(treeItem, this.children[index]);
68 },
69
70 /**
71 * Removes a tree item child.
72 * @param {!cr.ui.TreeItem} treeItem The tree item to remove.
73 */
74 remove: function(treeItem) {
75 this.removeChild(treeItem);
76 },
77
78 /**
79 * Handles click events on the tree and forwards the event to the relevant
80 * tree items as necesary.
81 * @param {Event} e The click event object.
82 */
83 handleClick: function(e) {
84 var treeItem = findTreeItem(e.target);
85 if (treeItem)
86 treeItem.handleClick(e);
87 },
88
89 handleMouseDown: function(e) {
90 if (e.button == 2) // right
91 this.handleClick(e);
92 },
93
94 /**
95 * Handles double click events on the tree.
96 * @param {Event} e The dblclick event object.
97 */
98 handleDblClick: function(e) {
99 var treeItem = findTreeItem(e.target);
100 if (treeItem)
101 treeItem.expanded = !treeItem.expanded;
102 },
103
104 /**
105 * Handles keydown events on the tree and updates selection and exanding
106 * of tree items.
107 * @param {Event} e The click event object.
108 */
109 handleKeyDown: function(e) {
110 var itemToSelect;
111 if (e.ctrlKey)
112 return;
113
114 var item = this.selectedItem;
115
116 var rtl = window.getComputedStyle(item).direction == 'rtl';
117
118 switch (e.keyIdentifier) {
119 case 'Up':
120 itemToSelect = item ? getPrevious(item) :
121 this.items[this.items.length - 1];
122 break;
123 case 'Down':
124 itemToSelect = item ? getNext(item) :
125 this.items[0];
126 break;
127 case 'Left':
128 case 'Right':
129 // Don't let back/forward keyboard shortcuts be used.
130 if (!cr.isMac && e.altKey || cr.isMac && e.metaKey)
131 break;
132
133 if (e.keyIdentifier == 'Left' && !rtl ||
134 e.keyIdentifier == 'Right' && rtl) {
135 if (item.expanded)
136 item.expanded = false;
137 else
138 itemToSelect = findTreeItem(item.parentNode);
139 } else {
140 if (!item.expanded)
141 item.expanded = true;
142 else
143 itemToSelect = item.items[0];
144 }
145 break;
146 case 'Home':
147 itemToSelect = this.items[0];
148 break;
149 case 'End':
150 itemToSelect = this.items[this.items.length - 1];
151 break;
152 }
153
154 if (itemToSelect) {
155 itemToSelect.selected = true;
156 e.preventDefault();
157 }
158 },
159
160 /**
161 * The selected tree item or null if none.
162 * @type {cr.ui.TreeItem}
163 */
164 get selectedItem() {
165 return this.selectedItem_ || null;
166 },
167 set selectedItem(item) {
168 var oldSelectedItem = this.selectedItem_;
169 if (oldSelectedItem != item) {
170 // Set the selectedItem_ before deselecting the old item since we only
171 // want one change when moving between items.
172 this.selectedItem_ = item;
173
174 if (oldSelectedItem)
175 oldSelectedItem.selected = false;
176
177 if (item)
178 item.selected = true;
179
180 cr.dispatchSimpleEvent(this, 'change');
181 }
182 }
183 };
184
185 /**
186 * This is used as a blueprint for new tree item elements.
187 * @type {!HTMLElement}
188 */
189 var treeItemProto = (function() {
190 var treeItem = cr.doc.createElement('div');
191 treeItem.className = 'tree-item';
192 treeItem.innerHTML = '<div class=tree-row>' +
193 '<span class=expand-icon></span>' +
194 '<span class=tree-label></span>' +
195 '</div>' +
196 '<div class=tree-children></div>';
197 return treeItem;
198 })();
199
200 /**
201 * Creates a new tree item.
202 * @param {Object=} opt_propertyBag Optional properties.
203 * @constructor
204 * @extends {HTMLElement}
205 */
206 var TreeItem = cr.ui.define(function() {
207 return treeItemProto.cloneNode(true);
208 });
209
210 TreeItem.prototype = {
211 __proto__: HTMLElement.prototype,
212
213 /**
214 * Initializes the element.
215 */
216 decorate: function() {
217
218 },
219
220 /**
221 * The tree items children.
222 */
223 get items() {
224 return this.lastElementChild.children;
225 },
226
227 /**
228 * Adds a tree item as a child.
229 * @param {!cr.ui.TreeItem} child The child to add.
230 */
231 add: function(child) {
232 this.addAt(child, 0xffffffff);
233 },
234
235 /**
236 * Adds a tree item as a child at a given index.
237 * @param {!cr.ui.TreeItem} child The child to add.
238 * @param {number} index The index where to add the child.
239 */
240 addAt: function(child, index) {
241 this.lastElementChild.insertBefore(child, this.items[index]);
242 if (this.items.length == 1)
243 this.hasChildren_ = true;
244 },
245
246 /**
247 * Removes a child.
248 * @param {!cr.ui.TreeItem} child The tree item child to remove.
249 */
250 remove: function(child) {
251 // If we removed the selected item we should become selected.
252 var tree = this.tree;
253 var selectedItem = tree.selectedItem;
254 if (selectedItem && child.contains(selectedItem))
255 this.selected = true;
256
257 this.lastElementChild.removeChild(child);
258 if (this.items.length == 0)
259 this.hasChildren_ = false;
260 },
261
262 /**
263 * The parent tree item.
264 * @type {!cr.ui.Tree|cr.ui.TreeItem}
265 */
266 get parentItem() {
267 var p = this.parentNode;
268 while (p && !(p instanceof TreeItem) && !(p instanceof Tree)) {
269 p = p.parentNode;
270 }
271 return p;
272 },
273
274 /**
275 * The tree that the tree item belongs to or null of no added to a tree.
276 * @type {cr.ui.Tree}
277 */
278 get tree() {
279 var t = this.parentItem;
280 while (t && !(t instanceof Tree)) {
281 t = t.parentItem;
282 }
283 return t;
284 },
285
286 /**
287 * Whether the tree item is expanded or not.
288 * @type {boolean}
289 */
290 get expanded() {
291 return this.hasAttribute('expanded');
292 },
293 set expanded(b) {
294 if (this.expanded == b)
295 return;
296
297 var treeChildren = this.lastElementChild;
298
299 if (b) {
300 if (this.mayHaveChildren_) {
301 this.setAttribute('expanded', '');
302 treeChildren.setAttribute('expanded', '');
303 cr.dispatchSimpleEvent(this, 'expand', true);
304 this.scrollIntoViewIfNeeded(false);
305 }
306 } else {
307 var tree = this.tree;
308 if (tree && !this.selected) {
309 var oldSelected = tree.selectedItem;
310 if (oldSelected && this.contains(oldSelected))
311 this.selected = true;
312 }
313 this.removeAttribute('expanded');
314 treeChildren.removeAttribute('expanded');
315 cr.dispatchSimpleEvent(this, 'collapse', true);
316 }
317 },
318
319 /**
320 * Expands all parent items.
321 */
322 reveal: function() {
323 var pi = this.parentItem;
324 while (pi && !(pi instanceof Tree)) {
325 pi.expanded = true;
326 pi = pi.parentItem;
327 }
328 },
329
330 /**
331 * The element representing the row that gets highlighted.
332 * @type {!HTMLElement}
333 */
334 get rowElement() {
335 return this.firstElementChild;
336 },
337
338 /**
339 * The element containing the label text and the icon.
340 * @type {!HTMLElement}
341 */
342 get labelElement() {
343 return this.firstElementChild.lastElementChild;
344 },
345
346 /**
347 * The label text.
348 * @type {string}
349 */
350 get label() {
351 return this.labelElement.textContent;
352 },
353 set label(s) {
354 this.labelElement.textContent = s;
355 },
356
357 /**
358 * The URL for the icon.
359 * @type {string}
360 */
361 get icon() {
362 return window.getComputedStyle(this.labelElement).
363 backgroundImage.slice(4, -1);
364 },
365 set icon(icon) {
366 return this.labelElement.style.backgroundImage = url(icon);
367 },
368
369 /**
370 * Whether the tree item is selected or not.
371 * @type {boolean}
372 */
373 get selected() {
374 return this.hasAttribute('selected');
375 },
376 set selected(b) {
377 if (this.selected == b)
378 return;
379 var rowItem = this.firstElementChild;
380 var tree = this.tree;
381 if (b) {
382 this.setAttribute('selected', '');
383 rowItem.setAttribute('selected', '');
384 this.labelElement.scrollIntoViewIfNeeded(false);
385 if (tree)
386 tree.selectedItem = this;
387 } else {
388 this.removeAttribute('selected');
389 rowItem.removeAttribute('selected');
390 if (tree && tree.selectedItem == this)
391 tree.selectedItem = null;
392 }
393 },
394
395 /**
396 * Whether the tree item has children.
397 * @type {boolean}
398 */
399 get mayHaveChildren_() {
400 return this.hasAttribute('may-have-children');
401 },
402 set mayHaveChildren_(b) {
403 var rowItem = this.firstElementChild;
404 if (b) {
405 this.setAttribute('may-have-children', '');
406 rowItem.setAttribute('may-have-children', '');
407 } else {
408 this.removeAttribute('may-have-children');
409 rowItem.removeAttribute('may-have-children');
410 }
411 },
412
413 /**
414 * Whether the tree item has children.
415 * @type {boolean}
416 */
417 get hasChildren() {
418 return !!this.items[0];
419 },
420
421 /**
422 * Whether the tree item has children.
423 * @type {boolean}
424 * @private
425 */
426 set hasChildren_(b) {
427 var rowItem = this.firstElementChild;
428 this.setAttribute('has-children', b);
429 rowItem.setAttribute('has-children', b);
430 if (b)
431 this.mayHaveChildren_ = true;
432 },
433
434 /**
435 * Called when the user clicks on a tree item. This is forwarded from the
436 * cr.ui.Tree.
437 * @param {Event} e The click event.
438 */
439 handleClick: function(e) {
440 if (e.target.className == 'expand-icon')
441 this.expanded = !this.expanded;
442 else
443 this.selected = true;
444 },
445
446 /**
447 * Makes the tree item user editable. If the user renamed the item a
448 * bubbling {@code rename} event is fired.
449 * @type {boolean}
450 */
451 set editing(editing) {
452 var oldEditing = this.editing;
453 if (editing == oldEditing)
454 return;
455
456 var self = this;
457 var labelEl = this.labelElement;
458 var text = this.label;
459 var input;
460
461 // Handles enter and escape which trigger reset and commit respectively.
462 function handleKeydown(e) {
463 // Make sure that the tree does not handle the key.
464 e.stopPropagation();
465
466 // Calling tree.focus blurs the input which will make the tree item
467 // non editable.
468 switch (e.keyIdentifier) {
469 case 'U+001B': // Esc
470 input.value = text;
471 // fall through
472 case 'Enter':
473 self.tree.focus();
474 }
475 }
476
477 function stopPropagation(e) {
478 e.stopPropagation();
479 }
480
481 if (editing) {
482 this.selected = true;
483 this.setAttribute('editing', '');
484 this.draggable = false;
485
486 // We create an input[type=text] and copy over the label value. When
487 // the input loses focus we set editing to false again.
488 input = this.ownerDocument.createElement('input');
489 input.value = text;
490 if (labelEl.firstChild)
491 labelEl.replaceChild(input, labelEl.firstChild);
492 else
493 labelEl.appendChild(input);
494
495 input.addEventListener('keydown', handleKeydown);
496 input.addEventListener('blur', cr.bind(function() {
497 this.editing = false;
498 }, this));
499
500 // Make sure that double clicks do not expand and collapse the tree
501 // item.
502 var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick'];
503 eventsToStop.forEach(function(type) {
504 input.addEventListener(type, stopPropagation);
505 });
506
507 input.focus();
508 input.select();
509 cr.ui.limitInputWidth(input, this.rowElement, 20);
510 // the padding and border of the tree-row
511
512 this.oldLabel_ = text;
513 } else {
514 this.removeAttribute('editing');
515 this.draggable = true;
516 input = labelEl.firstChild;
517 var value = input.value;
518 if (/^\s*$/.test(value)) {
519 labelEl.textContent = this.oldLabel_;
520 } else {
521 labelEl.textContent = value;
522 if (value != this.oldLabel_) {
523 cr.dispatchSimpleEvent(this, 'rename', true);
524 }
525 }
526 delete this.oldLabel_;
527 }
528 },
529
530 get editing() {
531 return this.hasAttribute('editing');
532 }
533 };
534
535 /**
536 * Helper function that returns the next visible tree item.
537 * @param {cr.ui.TreeItem} item The tree item.
538 * @retrun {cr.ui.TreeItem} The found item or null.
539 */
540 function getNext(item) {
541 if (item.expanded) {
542 var firstChild = item.items[0];
543 if (firstChild) {
544 return firstChild;
545 }
546 }
547
548 return getNextHelper(item);
549 }
550
551 /**
552 * Another helper function that returns the next visible tree item.
553 * @param {cr.ui.TreeItem} item The tree item.
554 * @retrun {cr.ui.TreeItem} The found item or null.
555 */
556 function getNextHelper(item) {
557 if (!item)
558 return null;
559
560 var nextSibling = item.nextElementSibling;
561 if (nextSibling) {
562 return nextSibling;
563 }
564 return getNextHelper(item.parentItem);
565 }
566
567 /**
568 * Helper function that returns the previous visible tree item.
569 * @param {cr.ui.TreeItem} item The tree item.
570 * @retrun {cr.ui.TreeItem} The found item or null.
571 */
572 function getPrevious(item) {
573 var previousSibling = item.previousElementSibling;
574 return previousSibling ? getLastHelper(previousSibling) : item.parentItem;
575 }
576
577 /**
578 * Helper function that returns the last visible tree item in the subtree.
579 * @param {cr.ui.TreeItem} item The item to find the last visible item for.
580 * @return {cr.ui.TreeItem} The found item or null.
581 */
582 function getLastHelper(item) {
583 if (!item)
584 return null;
585 if (item.expanded && item.hasChildren) {
586 var lastChild = item.items[item.items.length - 1];
587 return getLastHelper(lastChild);
588 }
589 return item;
590 }
591
592 // Export
593 return {
594 Tree: Tree,
595 TreeItem: TreeItem
596 };
597 });
OLDNEW
« no previous file with comments | « resources/bookmark_manager/js/cr/ui/menuitem.js ('k') | resources/bookmark_manager/js/i18ntemplate.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698