Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
|
mtomasz
2013/03/14 02:52:05
2012 -> 2013
yoshiki
2013/03/14 07:57:53
Done.
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * Update sub-elements of {@code parentElement} reading {@code DirectoryEntry} | |
| 7 * with calling {@code iterator}. | |
| 8 * | |
| 9 * @param {DirectoryItem|DirectoryTree} parentElement Parent element of newly | |
| 10 * created items. | |
| 11 * @param {function(number): DirectoryEntry} iterator Function which returns | |
| 12 * the n-th Entry in the directory. | |
| 13 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 14 */ | |
| 15 function updateSubElementsFromList(parentElement, iterator, directoryModel) { | |
| 16 var index = 0; | |
| 17 while (iterator(index)) { | |
| 18 var currentEntry = iterator(index); | |
| 19 var currentElement = parentElement.items[index]; | |
| 20 | |
| 21 if (index >= parentElement.items.length) { | |
| 22 var item = new DirectoryItem(currentEntry, parentElement, directoryModel); | |
| 23 parentElement.add(item); | |
| 24 index++; | |
| 25 } else if (currentEntry.fullPath === currentElement.fullPath) { | |
| 26 index++; | |
| 27 } else if (currentEntry.fullPath < currentElement.fullPath) { | |
| 28 var item = new DirectoryItem(currentEntry, parentElement, directoryModel); | |
| 29 parentElement.addAt(item, index); | |
| 30 index++; | |
| 31 } else if (currentEntry.fullPath > currentElement.fullPath) { | |
| 32 parentElement.remove(currentElement); | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 var removedChild; | |
| 37 while (removedChild = parentElement.items[index]) { | |
| 38 parentElement.remove(removedChild); | |
| 39 } | |
| 40 | |
| 41 if (index === 0) { | |
| 42 parentElement.hasChildren = false; | |
| 43 parentElement.expanded = false; | |
| 44 } else { | |
| 45 parentElement.hasChildren = true; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 /** | |
| 50 * @param {DirectoryEntry} parentDirEntry DirectoryEntry of this item. | |
| 51 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 52 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 53 * @extends {cr.ui.TreeItem} | |
| 54 * @constructor | |
| 55 */ | |
| 56 function DirectoryItem(parentDirEntry, parentDirItem, directoryModel) { | |
| 57 var item = cr.doc.createElement('div'); | |
| 58 DirectoryItem.decorate(item, parentDirEntry, parentDirItem, directoryModel); | |
| 59 return item; | |
| 60 } | |
| 61 | |
| 62 /** | |
| 63 * @param {HTMLElement} el Element to be DirectoryItem. | |
| 64 * @param {DirectoryEntry} parentDirEntry DirectoryEntry of this item. | |
| 65 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 66 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 67 */ | |
|
mtomasz
2013/03/14 02:52:05
Can we move to Class.prototype.method = function()
yoshiki
2013/03/14 07:57:53
Done.
| |
| 68 DirectoryItem.decorate = | |
| 69 function(el, parentDirEntry, parentDirItem, directoryModel) { | |
| 70 el.__proto__ = DirectoryItem.prototype; | |
| 71 (/** @type {DirectoryItem} */ el).decorate( | |
| 72 parentDirEntry, parentDirItem, directoryModel); | |
| 73 }; | |
| 74 | |
| 75 DirectoryItem.prototype = { | |
| 76 __proto__: cr.ui.TreeItem.prototype, | |
| 77 | |
| 78 /** | |
| 79 * @param {DirectoryEntry} parentDirEntry DirectoryEntry of this item. | |
| 80 * @param {DirectoryItem|DirectoryTree} parentDirItem Parent of this item. | |
| 81 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 82 */ | |
| 83 decorate: function(parentDirEntry, parentDirItem, directoryModel) { | |
| 84 var path = parentDirEntry.fullPath; | |
| 85 var label = PathUtil.isRootPath(path) ? | |
| 86 PathUtil.getRootLabel(path) : parentDirEntry.name; | |
| 87 | |
| 88 this.className = 'tree-item'; | |
| 89 this.innerHTML = | |
| 90 '<div class=tree-row>' + | |
| 91 '<span class=expand-icon></span>' + | |
| 92 '<span class=icon></span>' + | |
| 93 '<span class=label></span>' + | |
| 94 '<div class=root-eject></div>' + | |
| 95 '</div>' + | |
| 96 '<div class=tree-children></div>'; | |
| 97 this.setAttribute('role', 'treeitem'); | |
| 98 | |
| 99 this.directoryModel_ = directoryModel; | |
| 100 this.parent_ = parentDirItem; | |
| 101 this.label = label; | |
| 102 this.fullPath = path; | |
| 103 | |
| 104 // Sets hasChildren=true tentatively. This will be overridden after | |
| 105 // scanning sub-directories in updateSubElementsFromList. | |
| 106 this.hasChildren = true; | |
| 107 | |
| 108 this.addEventListener('expand', this.onExpand_.bind(this), true); | |
| 109 | |
| 110 var icon = this.querySelector('.icon'); | |
| 111 if (PathUtil.isRootPath(path)) { | |
| 112 var iconType = PathUtil.getRootType(path); | |
| 113 if (iconType === RootType.REMOVABLE) | |
| 114 iconType = this.volumeManager_.getDeviceType(path); | |
| 115 | |
| 116 icon.classList.add('volume-icon'); | |
| 117 icon.setAttribute('volume-type-icon', iconType); | |
| 118 } | |
| 119 | |
| 120 var eject = this.querySelector('.root-eject'); | |
| 121 eject.hidden = !PathUtil.isUnmountableByUser(path); | |
| 122 eject.addEventListener('click', | |
| 123 function(event) { | |
| 124 event.stopPropagation(); | |
|
mtomasz
2013/03/14 02:52:05
1. Is stopPropagation() necessary here?
2. Is chec
yoshiki
2013/03/14 07:57:53
Yes, it prevents selecting the item when user trys
yoshiki
2013/03/14 09:05:48
I found some bugs on removable device, but it is f
| |
| 125 if (!PathUtil.isUnmountableByUser(path)) | |
| 126 return; | |
| 127 | |
| 128 var volumeManager = VolumeManager.getInstance(); | |
| 129 volumeManager.unmount(path, function() {}, function() {}); | |
| 130 }.bind(this)); | |
| 131 | |
| 132 if ('expanded' in parentDirItem || parentDirItem.expanded) | |
| 133 this.updateSubDirectoriesWithEntry_(parentDirEntry); | |
| 134 }, | |
| 135 | |
| 136 /** | |
| 137 * The element containing the label text and the icon. | |
| 138 * @type {!HTMLElement} | |
| 139 * @override | |
| 140 **/ | |
| 141 get labelElement() { | |
| 142 return this.firstElementChild.querySelector('.label'); | |
|
mtomasz
2013/03/14 02:52:05
Indentation is off?
yoshiki
2013/03/14 07:57:53
Done.
| |
| 143 }, | |
| 144 | |
| 145 /** | |
| 146 * Invoked when the item is being expended. | |
|
mtomasz
2013/03/14 02:52:05
expended -> expanded
yoshiki
2013/03/14 07:57:53
Done.
| |
| 147 * @param {!UIEvent} e Event. | |
| 148 * @private | |
| 149 **/ | |
| 150 onExpand_: function(e) { | |
| 151 this.updateSubDirectories(function() { | |
| 152 this.expanded = false; | |
| 153 }); | |
|
mtomasz
2013/03/14 02:52:05
Add .bind(this)? UpdateSubDirectories calls resolv
yoshiki
2013/03/14 07:57:53
Done.
| |
| 154 | |
| 155 e.stopPropagation(); | |
|
mtomasz
2013/03/14 02:52:05
Is this necessary?
yoshiki
2013/03/14 07:57:53
It prevents selecting the item when clicking expan
| |
| 156 }, | |
| 157 | |
| 158 /** | |
|
mtomasz
2013/03/14 02:52:05
Please add a short description.
yoshiki
2013/03/14 07:57:53
Done.
| |
| 159 * @param {function()=} opt_errorCallback Callback called on error. | |
| 160 */ | |
| 161 updateSubDirectories: function(opt_errorCallback) { | |
| 162 this.directoryModel_.resolveDirectory( | |
| 163 this.fullPath, | |
| 164 function(entry) { | |
| 165 this.updateSubDirectoriesWithEntry_(entry, opt_errorCallback); | |
| 166 }, | |
|
mtomasz
2013/03/14 02:52:05
.bind(this) missing. Causes js errors.
yoshiki
2013/03/14 07:57:53
Done.
| |
| 167 opt_errorCallback); | |
| 168 }, | |
| 169 | |
| 170 /** | |
| 171 * @param {!DirectoryEntry} dirEntry DirectoryEntry to read from. | |
| 172 * @param {function()=} opt_errorCallback Callback called on error. | |
| 173 * @private | |
| 174 */ | |
| 175 updateSubDirectoriesWithEntry_: function(dirEntry, opt_errorCallback) { | |
| 176 // Skips if the entry is dummy. | |
| 177 if (!dirEntry.createReader) { | |
|
mtomasz
2013/03/14 02:52:05
nit: How about instanceof? Sounds cleaner and less
yoshiki
2013/03/14 07:57:53
Done.
| |
| 178 if (opt_errorCallback) | |
| 179 opt_errorCallback(); | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 var reader = dirEntry.createReader(); | |
| 184 var entries = []; | |
| 185 | |
| 186 var readEntry = function() { | |
| 187 reader.readEntries(function(results) { | |
| 188 if (results.length === 0) { | |
| 189 this.entries_ = entries.sort(); | |
| 190 this.redrawSubDirectoryList_(); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 for (var i = 0; i < results.length; i++) { | |
| 195 var entry = results[i]; | |
| 196 if (entry.isDirectory) | |
| 197 entries.push(entry); | |
| 198 } | |
| 199 readEntry(); | |
| 200 }.bind(this)); | |
| 201 }.bind(this); | |
| 202 readEntry(); | |
| 203 }, | |
| 204 | |
| 205 /** | |
| 206 * @private | |
| 207 */ | |
| 208 redrawSubDirectoryList_: function() { | |
| 209 var entries = this.entries_; | |
| 210 updateSubElementsFromList(this, | |
| 211 function(i) { return entries[i]; }, | |
| 212 this.directoryModel_); | |
| 213 } | |
| 214 }; | |
| 215 | |
| 216 /** | |
|
mtomasz
2013/03/14 02:52:05
I think we should have description for the classes
yoshiki
2013/03/14 07:57:53
Done.
| |
| 217 * @constructor | |
| 218 * @extends {cr.ui.Tree} | |
| 219 */ | |
| 220 function DirectoryTree() {} | |
| 221 | |
| 222 /** | |
| 223 * Decorate element. | |
|
mtomasz
2013/03/14 02:52:05
nit: I think we should consistently use passive fo
yoshiki
2013/03/14 07:57:53
Done.
| |
| 224 * @param {HTMLElement} el Element to be DirectoryTree. | |
| 225 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 226 */ | |
| 227 DirectoryTree.decorate = function(el, directoryModel) { | |
| 228 el.__proto__ = DirectoryTree.prototype; | |
| 229 (/** @type {DirectoryTree} */ el).decorate(directoryModel); | |
|
mtomasz
2013/03/14 02:52:05
I think this virtual cast is unnecessary, since el
yoshiki
2013/03/14 07:57:53
This does nothing on JavaScript interpreter. It's
| |
| 230 }; | |
| 231 | |
| 232 DirectoryTree.prototype = { | |
| 233 __proto__: cr.ui.Tree.prototype, | |
| 234 | |
| 235 /** | |
| 236 * Decorate element. | |
| 237 * @param {DirectoryModel} directoryModel Current DirectoryModel. | |
| 238 */ | |
| 239 decorate: function(directoryModel) { | |
| 240 cr.ui.Tree.prototype.decorate.call(this); | |
| 241 | |
| 242 this.directoryModel_ = directoryModel; | |
| 243 | |
| 244 this.rootsList_ = this.directoryModel_.getRootsList(); | |
| 245 this.rootsList_.addEventListener('change', | |
| 246 this.onRootsListChanged_.bind(this)); | |
| 247 this.rootsList_.addEventListener('permuted', | |
| 248 this.onRootsListChanged_.bind(this)); | |
| 249 this.onRootsListChanged_(); | |
| 250 }, | |
| 251 | |
| 252 /** | |
| 253 * @param {cr.ui.Menu} menu Context menu. | |
| 254 */ | |
| 255 setContextMenu: function(menu) { | |
| 256 this.contextMenu_ = menu; | |
| 257 | |
| 258 for (var i = 0; i < this.rootsList_.length; i++) { | |
| 259 var item = this.rootsList_.item(i); | |
| 260 var type = PathUtil.getRootType(item.fullPath); | |
| 261 // Context menu is set only to archive and removable volumes. | |
| 262 if (type === RootType.ARCHIVE || type === RootType.REMOVABLE) { | |
| 263 cr.ui.contextMenuHandler.setContextMenu(this.items[i].rowElement, | |
| 264 this.contextMenu_); | |
| 265 } | |
| 266 } | |
| 267 }, | |
| 268 | |
| 269 /** | |
| 270 * Invoked when the root list is changed. | |
| 271 * @private | |
| 272 */ | |
| 273 onRootsListChanged_: function() { | |
| 274 var rootsList = this.rootsList_; | |
| 275 updateSubElementsFromList(this, | |
| 276 rootsList.item.bind(rootsList), | |
| 277 this.directoryModel_); | |
| 278 this.setContextMenu(this.contextMenu_); | |
| 279 }, | |
| 280 | |
| 281 /** | |
| 282 * Returns the path of the current selected item. | |
| 283 * @return {string} The current path. | |
| 284 */ | |
| 285 getCurrentPath: function() { | |
| 286 return this.selectedItem ? this.selectedItem.fullPath : null; | |
| 287 } | |
| 288 }; | |
| OLD | NEW |