blob: f889786a39ac680c5f112e57c21b6fa2db9a2460 [file] [log] [blame]
// Copyright 2016 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.
import 'chrome://resources/cr_components/managed_footnote/managed_footnote.js';
import 'chrome://resources/cr_elements/shared_vars_css.m.js';
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.m.js';
import 'chrome://resources/cr_elements/cr_splitter/cr_splitter.js';
import './folder_node.js';
import './list.js';
import './router.js';
import './shared_vars.js';
import './strings.m.js';
import './toolbar.js';
import {FindShortcutBehavior} from 'chrome://resources/cr_elements/find_shortcut_behavior.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {setSearchResults} from './actions.js';
import {destroy as destroyApiListener, init as initApiListener} from './api_listener.js';
import {CommandManager} from './command_manager.js';
import {LOCAL_STORAGE_FOLDER_STATE_KEY, LOCAL_STORAGE_TREE_WIDTH_KEY, ROOT_NODE_ID} from './constants.js';
import {DNDManager} from './dnd_manager.js';
import {MouseFocusBehavior} from './mouse_focus_behavior.js';
import {Store} from './store.js';
import {StoreClient} from './store_client.js';
import {FolderOpenState} from './types.js';
import {createEmptyState, normalizeNodes} from './util.js';
Polymer({
is: 'bookmarks-app',
_template: html`{__html_template__}`,
behaviors: [
MouseFocusBehavior,
StoreClient,
FindShortcutBehavior,
],
properties: {
/** @private */
searchTerm_: {
type: String,
observer: 'searchTermChanged_',
},
/** @type {FolderOpenState} */
folderOpenState_: {
type: Object,
observer: 'folderOpenStateChanged_',
},
/** @private */
sidebarWidth_: String,
},
/** @private{?function(!Event)} */
boundUpdateSidebarWidth_: null,
/** @private {DNDManager} */
dndManager_: null,
/** @override */
created() {
// Regular expression that captures the leading slash, the content and the
// trailing slash in three different groups.
const CANONICAL_PATH_REGEX = /(^\/)([\/-\w]+)(\/$)/;
const path = location.pathname.replace(CANONICAL_PATH_REGEX, '$1$2');
if (path !== '/') { // Only queries are supported, not subpages.
window.history.replaceState(undefined /* stateObject */, '', '/');
}
},
/** @override */
attached() {
document.documentElement.classList.remove('loading');
this.watch('searchTerm_', function(state) {
return state.search.term;
});
this.watch('folderOpenState_', function(state) {
return state.folderOpenState;
});
chrome.bookmarks.getTree((results) => {
const nodeMap = normalizeNodes(results[0]);
const initialState = createEmptyState();
initialState.nodes = nodeMap;
initialState.selectedFolder = nodeMap[ROOT_NODE_ID].children[0];
const folderStateString =
window.localStorage[LOCAL_STORAGE_FOLDER_STATE_KEY];
initialState.folderOpenState = folderStateString ?
new Map(
/** @type Array<Array<boolean|string>> */ (
JSON.parse(folderStateString))) :
new Map();
Store.getInstance().init(initialState);
initApiListener();
setTimeout(function() {
chrome.metricsPrivate.recordTime(
'BookmarkManager.ResultsRenderedTime',
Math.floor(window.performance.now()));
});
});
this.boundUpdateSidebarWidth_ = this.updateSidebarWidth_.bind(this);
this.initializeSplitter_();
this.dndManager_ = new DNDManager();
this.dndManager_.init();
},
detached() {
window.removeEventListener('resize', this.boundUpdateSidebarWidth_);
this.dndManager_.destroy();
destroyApiListener();
},
/**
* Set up the splitter and set the initial width from localStorage.
* @private
*/
initializeSplitter_() {
const splitter = this.$.splitter;
const splitterTarget = this.$.sidebar;
// The splitter persists the size of the left component in the local store.
if (LOCAL_STORAGE_TREE_WIDTH_KEY in window.localStorage) {
splitterTarget.style.width =
window.localStorage[LOCAL_STORAGE_TREE_WIDTH_KEY];
}
this.sidebarWidth_ =
/** @type {string} */ (getComputedStyle(splitterTarget).width);
splitter.addEventListener('resize', (e) => {
window.localStorage[LOCAL_STORAGE_TREE_WIDTH_KEY] =
splitterTarget.style.width;
this.updateSidebarWidth_();
});
splitter.addEventListener('dragmove', this.boundUpdateSidebarWidth_);
window.addEventListener('resize', this.boundUpdateSidebarWidth_);
},
/** @private */
updateSidebarWidth_() {
this.sidebarWidth_ =
/** @type {string} */ (getComputedStyle(this.$.sidebar).width);
},
/** @private */
searchTermChanged_(newValue, oldValue) {
if (oldValue !== undefined && !newValue) {
this.fire(
'iron-announce', {text: loadTimeData.getString('searchCleared')});
}
if (!this.searchTerm_) {
return;
}
chrome.bookmarks.search(this.searchTerm_, (results) => {
const ids = results.map(function(node) {
return node.id;
});
this.dispatch(setSearchResults(ids));
this.fire('iron-announce', {
text: ids.length > 0 ?
loadTimeData.getStringF('searchResults', this.searchTerm_) :
loadTimeData.getString('noSearchResults')
});
});
},
/** @private */
folderOpenStateChanged_() {
window.localStorage[LOCAL_STORAGE_FOLDER_STATE_KEY] =
JSON.stringify(Array.from(this.folderOpenState_));
},
// Override FindShortcutBehavior methods.
handleFindShortcut(modalContextOpen) {
if (modalContextOpen) {
return false;
}
this.$$('bookmarks-toolbar').searchField.showAndFocus();
return true;
},
// Override FindShortcutBehavior methods.
searchInputHasFocus() {
return this.$$('bookmarks-toolbar').searchField.isSearchFocused();
},
/** @private */
onUndoClick_() {
this.fire('command-undo');
},
});