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 /** @const */ var BookmarkList = bmm.BookmarkList; | 5 /** @const */ var BookmarkList = bmm.BookmarkList; |
6 /** @const */ var BookmarkTree = bmm.BookmarkTree; | 6 /** @const */ var BookmarkTree = bmm.BookmarkTree; |
7 /** @const */ var Command = cr.ui.Command; | 7 /** @const */ var Command = cr.ui.Command; |
8 /** @const */ var CommandBinding = cr.ui.CommandBinding; | 8 /** @const */ var CommandBinding = cr.ui.CommandBinding; |
9 /** @const */ var LinkKind = cr.LinkKind; | 9 /** @const */ var LinkKind = cr.LinkKind; |
10 /** @const */ var ListItem = cr.ui.ListItem; | 10 /** @const */ var ListItem = cr.ui.ListItem; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 | 93 |
94 var recentTreeItem = new TreeItem({ | 94 var recentTreeItem = new TreeItem({ |
95 icon: 'images/bookmark_manager_recent.png', | 95 icon: 'images/bookmark_manager_recent.png', |
96 bookmarkId: 'recent' | 96 bookmarkId: 'recent' |
97 }); | 97 }); |
98 bmm.treeLookup[recentTreeItem.bookmarkId] = recentTreeItem; | 98 bmm.treeLookup[recentTreeItem.bookmarkId] = recentTreeItem; |
99 | 99 |
100 BookmarkTree.decorate(tree); | 100 BookmarkTree.decorate(tree); |
101 | 101 |
102 tree.addEventListener('change', function() { | 102 tree.addEventListener('change', function() { |
103 navigateTo(tree.selectedItem.bookmarkId); | 103 navigateTo(tree.selectedItem.bookmarkId, updateHash); |
104 }); | 104 }); |
105 | 105 |
106 /** | 106 /** |
107 * Adds an event listener to a node that will remove itself after firing once. | 107 * Adds an event listener to a node that will remove itself after firing once. |
108 * @param {!Element} node The DOM node to add the listener to. | 108 * @param {!Element} node The DOM node to add the listener to. |
109 * @param {string} name The name of the event listener to add to. | 109 * @param {string} name The name of the event listener to add to. |
110 * @param {function(Event)} handler Function called when the event fires. | 110 * @param {function(Event)} handler Function called when the event fires. |
111 */ | 111 */ |
112 function addOneShotEventListener(node, name, handler) { | 112 function addOneShotEventListener(node, name, handler) { |
113 var f = function(e) { | 113 var f = function(e) { |
114 handler(e); | 114 handler(e); |
115 node.removeEventListener(name, f); | 115 node.removeEventListener(name, f); |
116 }; | 116 }; |
117 node.addEventListener(name, f); | 117 node.addEventListener(name, f); |
118 } | 118 } |
119 | 119 |
120 /** | 120 /** |
121 * Updates the location hash to reflect the current state of the application. | 121 * Updates the location hash to reflect the current state of the application. |
122 */ | 122 */ |
123 function updateHash() { | 123 function updateHash() { |
124 window.location.hash = tree.selectedItem.bookmarkId; | 124 window.location.hash = tree.selectedItem.bookmarkId; |
125 } | 125 } |
126 | 126 |
127 /** | 127 /** |
128 * Navigates to a bookmark ID. | 128 * Navigates to a bookmark ID. |
129 * @param {string} id The ID to navigate to. | 129 * @param {string} id The ID to navigate to. |
130 * @param {boolean=} opt_updateHashNow Whether to immediately update the | 130 * @param {function()} callback Function called when list view loaded or |
131 * location.hash. If false, then it is updated in a timeout. | 131 * displayed specified folder. |
132 */ | 132 */ |
133 function navigateTo(id, opt_updateHashNow) { | 133 function navigateTo(id, callback) { |
134 // console.info('navigateTo', 'from', window.location.hash, 'to', id); | 134 if (list.parentId == id) { |
135 // Update the location hash using a timer to prevent reentrancy. This is how | 135 callback(); |
136 // often we add history entries and the time here is a bit arbitrary but was | 136 return; |
137 // picked as the smallest time a human perceives as instant. | 137 } |
138 | 138 |
139 clearTimeout(navigateTo.timer_); | 139 addOneShotEventListener(list, 'load', callback); |
140 if (opt_updateHashNow) | |
141 updateHash(); | |
142 else | |
143 navigateTo.timer_ = setTimeout(updateHash, 250); | |
144 | |
145 updateParentId(id); | 140 updateParentId(id); |
146 } | 141 } |
147 | 142 |
148 /** | 143 /** |
149 * Updates the parent ID of the bookmark list and selects the correct tree item. | 144 * Updates the parent ID of the bookmark list and selects the correct tree item. |
150 * @param {string} id The id. | 145 * @param {string} id The id. |
151 */ | 146 */ |
152 function updateParentId(id) { | 147 function updateParentId(id) { |
| 148 // Setting list.parentId fires 'load' event. |
153 list.parentId = id; | 149 list.parentId = id; |
154 if (id in bmm.treeLookup) | 150 |
155 tree.selectedItem = bmm.treeLookup[id]; | 151 // When tree.selectedItem changed, tree view calls navigatTo() then it |
| 152 // calls updateHash() when list view displayed specified folder. |
| 153 tree.selectedItem = bmm.treeLookup[id] || tree.selectedItem; |
156 } | 154 } |
157 | 155 |
158 // Process the location hash. This is called by onhashchange and when the page | 156 // Process the location hash. This is called by onhashchange and when the page |
159 // is first loaded. | 157 // is first loaded. |
160 function processHash() { | 158 function processHash() { |
161 var id = window.location.hash.slice(1); | 159 var id = window.location.hash.slice(1); |
162 if (!id) { | 160 if (!id) { |
163 // If we do not have a hash, select first item in the tree. | 161 // If we do not have a hash, select first item in the tree. |
164 id = tree.items[0].bookmarkId; | 162 id = tree.items[0].bookmarkId; |
165 } | 163 } |
(...skipping 12 matching lines...) Expand all Loading... |
178 // After the list reloads, edit the desired bookmark. | 176 // After the list reloads, edit the desired bookmark. |
179 var editBookmark = function(e) { | 177 var editBookmark = function(e) { |
180 var index = list.dataModel.findIndexById(bookmarkNode.id); | 178 var index = list.dataModel.findIndexById(bookmarkNode.id); |
181 if (index != -1) { | 179 if (index != -1) { |
182 var sm = list.selectionModel; | 180 var sm = list.selectionModel; |
183 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; | 181 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; |
184 scrollIntoViewAndMakeEditable(index); | 182 scrollIntoViewAndMakeEditable(index); |
185 } | 183 } |
186 }; | 184 }; |
187 | 185 |
188 if (list.parentId == bookmarkNode.parentId) { | 186 navigateTo(bookmarkNode.parentId, editBookmark); |
189 // Clear the e= from the hash so that future attemps to edit the same | |
190 // entry will show up as a hash change. | |
191 updateHash(); | |
192 editBookmark(); | |
193 } else { | |
194 // Navigate to the parent folder (which will update the hash). Once | |
195 // it's loaded, edit the bookmark. | |
196 addOneShotEventListener(list, 'load', editBookmark); | |
197 updateParentId(bookmarkNode.parentId); | |
198 } | |
199 }); | 187 }); |
200 | 188 |
201 // We handle the two cases of navigating to the bookmark to be edited | 189 // We handle the two cases of navigating to the bookmark to be edited |
202 // above. Don't run the standard navigation code below. | 190 // above. Don't run the standard navigation code below. |
203 return; | 191 return; |
204 } else if (/^q=/.test(id)) { | 192 } else if (/^q=/.test(id)) { |
205 // In case we got a search hash, update the text input and the | 193 // In case we got a search hash, update the text input and the |
206 // bmm.treeLookup to use the new id. | 194 // bmm.treeLookup to use the new id. |
207 setSearch(id.slice(2)); | 195 setSearch(id.slice(2)); |
208 valid = true; | 196 valid = true; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 if (searchText) { | 252 if (searchText) { |
265 tree.add(searchTreeItem); | 253 tree.add(searchTreeItem); |
266 tree.selectedItem = searchTreeItem; | 254 tree.selectedItem = searchTreeItem; |
267 } else { | 255 } else { |
268 // Go "home". | 256 // Go "home". |
269 tree.selectedItem = tree.items[0]; | 257 tree.selectedItem = tree.items[0]; |
270 id = tree.selectedItem.bookmarkId; | 258 id = tree.selectedItem.bookmarkId; |
271 } | 259 } |
272 | 260 |
273 // Navigate now and update hash immediately. | 261 // Navigate now and update hash immediately. |
274 navigateTo(id, true); | 262 navigateTo(id, updateHash); |
275 } | 263 } |
276 | 264 |
277 // Handle the logo button UI. | 265 // Handle the logo button UI. |
278 // When the user clicks the button we should navigate "home" and focus the list. | 266 // When the user clicks the button we should navigate "home" and focus the list. |
279 document.querySelector('button.logo').onclick = function(e) { | 267 document.querySelector('button.logo').onclick = function(e) { |
280 setSearch(''); | 268 setSearch(''); |
281 $('list').focus(); | 269 $('list').focus(); |
282 }; | 270 }; |
283 | 271 |
284 /** | 272 /** |
(...skipping 1009 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1294 dataModel.splice(index, 1); | 1282 dataModel.splice(index, 1); |
1295 } | 1283 } |
1296 }); | 1284 }); |
1297 | 1285 |
1298 /** | 1286 /** |
1299 * Navigates to the folder that the selected item is in and selects it. This is | 1287 * Navigates to the folder that the selected item is in and selects it. This is |
1300 * used for the show-in-folder command. | 1288 * used for the show-in-folder command. |
1301 */ | 1289 */ |
1302 function showInFolder() { | 1290 function showInFolder() { |
1303 var bookmarkNode = list.selectedItem; | 1291 var bookmarkNode = list.selectedItem; |
| 1292 if (!bookmarkNode) |
| 1293 return; |
1304 var parentId = bookmarkNode.parentId; | 1294 var parentId = bookmarkNode.parentId; |
1305 | 1295 |
1306 // After the list is loaded we should select the revealed item. | 1296 // After the list is loaded we should select the revealed item. |
1307 function f(e) { | 1297 function selectItem() { |
1308 var index; | 1298 var index = list.dataModel.findIndexById(bookmarkNode.id); |
1309 if (bookmarkNode && | 1299 if (index == -1) |
1310 (index = list.dataModel.findIndexById(bookmarkNode.id)) != -1) { | 1300 return; |
1311 var sm = list.selectionModel; | 1301 var sm = list.selectionModel; |
1312 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; | 1302 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; |
1313 list.scrollIndexIntoView(index); | 1303 list.scrollIndexIntoView(index); |
1314 } | |
1315 list.removeEventListener('load', f); | |
1316 } | 1304 } |
1317 list.addEventListener('load', f); | 1305 |
1318 var treeItem = bmm.treeLookup[parentId]; | 1306 var treeItem = bmm.treeLookup[parentId]; |
1319 treeItem.reveal(); | 1307 treeItem.reveal(); |
1320 | 1308 |
1321 navigateTo(parentId); | 1309 navigateTo(parentId, selectItem); |
1322 } | 1310 } |
1323 | 1311 |
1324 var linkController; | 1312 var linkController; |
1325 | 1313 |
1326 /** | 1314 /** |
1327 * @return {!cr.LinkController} The link controller used to open links based on | 1315 * @return {!cr.LinkController} The link controller used to open links based on |
1328 * user clicks and keyboard actions. | 1316 * user clicks and keyboard actions. |
1329 */ | 1317 */ |
1330 function getLinkController() { | 1318 function getLinkController() { |
1331 return linkController || | 1319 return linkController || |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1402 }); | 1390 }); |
1403 } | 1391 } |
1404 | 1392 |
1405 /** | 1393 /** |
1406 * Opens an item in the list. | 1394 * Opens an item in the list. |
1407 */ | 1395 */ |
1408 function openItem() { | 1396 function openItem() { |
1409 var bookmarkNodes = getSelectedBookmarkNodes(); | 1397 var bookmarkNodes = getSelectedBookmarkNodes(); |
1410 // If we double clicked or pressed enter on a single folder, navigate to it. | 1398 // If we double clicked or pressed enter on a single folder, navigate to it. |
1411 if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0])) { | 1399 if (bookmarkNodes.length == 1 && bmm.isFolder(bookmarkNodes[0])) { |
1412 navigateTo(bookmarkNodes[0].id); | 1400 navigateTo(bookmarkNodes[0].id, updateHash); |
1413 } else { | 1401 } else { |
1414 openBookmarks(LinkKind.FOREGROUND_TAB); | 1402 openBookmarks(LinkKind.FOREGROUND_TAB); |
1415 } | 1403 } |
1416 } | 1404 } |
1417 | 1405 |
1418 /** | 1406 /** |
1419 * Deletes the selected bookmarks. The bookmarks are saved in memory in case | 1407 * Deletes the selected bookmarks. The bookmarks are saved in memory in case |
1420 * the user needs to undo the deletion. | 1408 * the user needs to undo the deletion. |
1421 */ | 1409 */ |
1422 function deleteBookmarks() { | 1410 function deleteBookmarks() { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1510 // Callback is called after tree and list data model updated. | 1498 // Callback is called after tree and list data model updated. |
1511 function createFolder(callback) { | 1499 function createFolder(callback) { |
1512 chrome.bookmarks.create({ | 1500 chrome.bookmarks.create({ |
1513 title: loadTimeData.getString('new_folder_name'), | 1501 title: loadTimeData.getString('new_folder_name'), |
1514 parentId: parentId | 1502 parentId: parentId |
1515 }, callback); | 1503 }, callback); |
1516 } | 1504 } |
1517 | 1505 |
1518 if (document.activeElement == tree) { | 1506 if (document.activeElement == tree) { |
1519 createFolder(function(newNode) { | 1507 createFolder(function(newNode) { |
1520 newItem = bmm.treeLookup[newNode.id]; | 1508 navigateTo(newNode.id, function() { |
1521 tree.selectedItem = newItem; | 1509 bmm.treeLookup[newNode.id].editing = true; |
1522 newItem.editing = true; | 1510 }); |
1523 }); | 1511 }); |
1524 return; | 1512 return; |
1525 } | 1513 } |
1526 | 1514 |
1527 function editNewFolderInList() { | 1515 function editNewFolderInList() { |
1528 createFolder(function() { | 1516 createFolder(function() { |
1529 var index = list.dataModel.length - 1; | 1517 var index = list.dataModel.length - 1; |
1530 var sm = list.selectionModel; | 1518 var sm = list.selectionModel; |
1531 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; | 1519 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = index; |
1532 scrollIntoViewAndMakeEditable(index); | 1520 scrollIntoViewAndMakeEditable(index); |
1533 }); | 1521 }); |
1534 } | 1522 } |
1535 | 1523 |
1536 if (parentId == list.parentId) { | 1524 navigateTo(parentId, editNewFolderInList); |
1537 editNewFolderInList(); | |
1538 return; | |
1539 } | |
1540 | |
1541 addOneShotEventListener(list, 'load', editNewFolderInList); | |
1542 navigateTo(parentId, true); | |
1543 } | 1525 } |
1544 | 1526 |
1545 /** | 1527 /** |
1546 * Scrolls the list item into view and makes it editable. | 1528 * Scrolls the list item into view and makes it editable. |
1547 * @param {number} index The index of the item to make editable. | 1529 * @param {number} index The index of the item to make editable. |
1548 */ | 1530 */ |
1549 function scrollIntoViewAndMakeEditable(index) { | 1531 function scrollIntoViewAndMakeEditable(index) { |
1550 list.scrollIndexIntoView(index); | 1532 list.scrollIndexIntoView(index); |
1551 // onscroll is now dispatched asynchronously so we have to postpone | 1533 // onscroll is now dispatched asynchronously so we have to postpone |
1552 // the rest. | 1534 // the rest. |
(...skipping 19 matching lines...) Expand all Loading... |
1572 id: 'new' | 1554 id: 'new' |
1573 }; | 1555 }; |
1574 var dataModel = list.dataModel; | 1556 var dataModel = list.dataModel; |
1575 var length = dataModel.length; | 1557 var length = dataModel.length; |
1576 dataModel.splice(length, 0, fakeNode); | 1558 dataModel.splice(length, 0, fakeNode); |
1577 var sm = list.selectionModel; | 1559 var sm = list.selectionModel; |
1578 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = length; | 1560 sm.anchorIndex = sm.leadIndex = sm.selectedIndex = length; |
1579 scrollIntoViewAndMakeEditable(length); | 1561 scrollIntoViewAndMakeEditable(length); |
1580 }; | 1562 }; |
1581 | 1563 |
1582 if (parentId == list.parentId) { | 1564 navigateTo(parentId, editNewBookmark); |
1583 editNewBookmark(); | |
1584 return; | |
1585 } | |
1586 | |
1587 addOneShotEventListener(list, 'load', editNewBookmark); | |
1588 navigateTo(parentId, true); | |
1589 } | 1565 } |
1590 | 1566 |
1591 /** | 1567 /** |
1592 * This function is used to select items after a user action such as paste, drop | 1568 * This function is used to select items after a user action such as paste, drop |
1593 * add page etc. | 1569 * add page etc. |
1594 * @param {BookmarkList|BookmarkTree} target The target of the user action. | 1570 * @param {BookmarkList|BookmarkTree} target The target of the user action. |
1595 * @param {=string} opt_selectedTreeId If provided, then select that tree id. | 1571 * @param {=string} opt_selectedTreeId If provided, then select that tree id. |
1596 */ | 1572 */ |
1597 function selectItemsAfterUserAction(target, opt_selectedTreeId) { | 1573 function selectItemsAfterUserAction(target, opt_selectedTreeId) { |
1598 // We get one onCreated event per item so we delay the handling until we get | 1574 // We get one onCreated event per item so we delay the handling until we get |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1745 document.addEventListener('copy', handle('copy-command')); | 1721 document.addEventListener('copy', handle('copy-command')); |
1746 document.addEventListener('cut', handle('cut-command')); | 1722 document.addEventListener('cut', handle('cut-command')); |
1747 | 1723 |
1748 var pasteHandler = handle('paste-command'); | 1724 var pasteHandler = handle('paste-command'); |
1749 document.addEventListener('paste', function(e) { | 1725 document.addEventListener('paste', function(e) { |
1750 // Paste is a bit special since we need to do an async call to see if we can | 1726 // Paste is a bit special since we need to do an async call to see if we can |
1751 // paste because the paste command might not be up to date. | 1727 // paste because the paste command might not be up to date. |
1752 updatePasteCommand(pasteHandler); | 1728 updatePasteCommand(pasteHandler); |
1753 }); | 1729 }); |
1754 })(); | 1730 })(); |
OLD | NEW |