diff options
Diffstat (limited to 'toolkit/jetpack/sdk/ui/sidebar')
-rw-r--r-- | toolkit/jetpack/sdk/ui/sidebar/actions.js | 10 | ||||
-rw-r--r-- | toolkit/jetpack/sdk/ui/sidebar/contract.js | 27 | ||||
-rw-r--r-- | toolkit/jetpack/sdk/ui/sidebar/namespace.js | 15 | ||||
-rw-r--r-- | toolkit/jetpack/sdk/ui/sidebar/utils.js | 8 | ||||
-rw-r--r-- | toolkit/jetpack/sdk/ui/sidebar/view.js | 214 |
5 files changed, 274 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/ui/sidebar/actions.js b/toolkit/jetpack/sdk/ui/sidebar/actions.js new file mode 100644 index 000000000..4a52984c9 --- /dev/null +++ b/toolkit/jetpack/sdk/ui/sidebar/actions.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const method = require('../../../method/core'); + +exports.show = method('show'); +exports.hide = method('hide'); +exports.toggle = method('toggle'); diff --git a/toolkit/jetpack/sdk/ui/sidebar/contract.js b/toolkit/jetpack/sdk/ui/sidebar/contract.js new file mode 100644 index 000000000..b59c37c0b --- /dev/null +++ b/toolkit/jetpack/sdk/ui/sidebar/contract.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { contract } = require('../../util/contract'); +const { isValidURI, URL, isLocalURL } = require('../../url'); +const { isNil, isObject, isString } = require('../../lang/type'); + +exports.contract = contract({ + id: { + is: [ 'string', 'undefined' ], + ok: v => /^[a-z0-9-_]+$/i.test(v), + msg: 'The option "id" must be a valid alphanumeric id (hyphens and ' + + 'underscores are allowed).' + }, + title: { + is: [ 'string' ], + ok: v => v.length + }, + url: { + is: [ 'string' ], + ok: v => isLocalURL(v), + map: v => v.toString(), + msg: 'The option "url" must be a valid local URI.' + } +}); diff --git a/toolkit/jetpack/sdk/ui/sidebar/namespace.js b/toolkit/jetpack/sdk/ui/sidebar/namespace.js new file mode 100644 index 000000000..d79725d1a --- /dev/null +++ b/toolkit/jetpack/sdk/ui/sidebar/namespace.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const models = exports.models = new WeakMap(); +const views = exports.views = new WeakMap(); +exports.buttons = new WeakMap(); + +exports.viewsFor = function viewsFor(sidebar) { + return views.get(sidebar); +}; +exports.modelFor = function modelFor(sidebar) { + return models.get(sidebar); +}; diff --git a/toolkit/jetpack/sdk/ui/sidebar/utils.js b/toolkit/jetpack/sdk/ui/sidebar/utils.js new file mode 100644 index 000000000..d6145c32e --- /dev/null +++ b/toolkit/jetpack/sdk/ui/sidebar/utils.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const method = require('../../../method/core'); + +exports.isShowing = method('isShowing'); diff --git a/toolkit/jetpack/sdk/ui/sidebar/view.js b/toolkit/jetpack/sdk/ui/sidebar/view.js new file mode 100644 index 000000000..c91e69d3d --- /dev/null +++ b/toolkit/jetpack/sdk/ui/sidebar/view.js @@ -0,0 +1,214 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'unstable', + 'engines': { + 'Firefox': '*' + } +}; + +const { models, buttons, views, viewsFor, modelFor } = require('./namespace'); +const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../../window/utils'); +const { setStateFor } = require('../state'); +const { defer } = require('../../core/promise'); +const { isPrivateBrowsingSupported, data } = require('../../self'); + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +const WEB_PANEL_BROWSER_ID = 'web-panels-browser'; + +const resolveURL = (url) => url ? data.url(url) : url; + +function create(window, details) { + let id = makeID(details.id); + let { document } = window; + + if (document.getElementById(id)) + throw new Error('The ID "' + details.id + '" seems already used.'); + + let menuitem = document.createElementNS(XUL_NS, 'menuitem'); + menuitem.setAttribute('id', id); + menuitem.setAttribute('label', details.title); + menuitem.setAttribute('sidebarurl', resolveURL(details.sidebarurl)); + menuitem.setAttribute('checked', 'false'); + menuitem.setAttribute('type', 'checkbox'); + menuitem.setAttribute('group', 'sidebar'); + menuitem.setAttribute('autoCheck', 'false'); + + document.getElementById('viewSidebarMenu').appendChild(menuitem); + + return menuitem; +} +exports.create = create; + +function dispose(menuitem) { + menuitem.parentNode.removeChild(menuitem); +} +exports.dispose = dispose; + +function updateTitle(sidebar, title) { + let button = buttons.get(sidebar); + + for (let window of windows(null, { includePrivate: true })) { + let { document } = window; + + // update the button + if (button) { + setStateFor(button, window, { label: title }); + } + + // update the menuitem + let mi = document.getElementById(makeID(sidebar.id)); + if (mi) { + mi.setAttribute('label', title) + } + + // update sidebar, if showing + if (isSidebarShowing(window, sidebar)) { + document.getElementById('sidebar-title').setAttribute('value', title); + } + } +} +exports.updateTitle = updateTitle; + +function updateURL(sidebar, url) { + let eleID = makeID(sidebar.id); + + url = resolveURL(url); + + for (let window of windows(null, { includePrivate: true })) { + // update the menuitem + let mi = window.document.getElementById(eleID); + if (mi) { + mi.setAttribute('sidebarurl', url) + } + + // update sidebar, if showing + if (isSidebarShowing(window, sidebar)) { + showSidebar(window, sidebar, url); + } + } +} +exports.updateURL = updateURL; + +function isSidebarShowing(window, sidebar) { + let win = window || getMostRecentBrowserWindow(); + + // make sure there is a window + if (!win) { + return false; + } + + // make sure there is a sidebar for the window + let sb = win.document.getElementById('sidebar'); + let sidebarTitle = win.document.getElementById('sidebar-title'); + if (!(sb && sidebarTitle)) { + return false; + } + + // checks if the sidebar box is hidden + let sbb = win.document.getElementById('sidebar-box'); + if (!sbb || sbb.hidden) { + return false; + } + + if (sidebarTitle.value == modelFor(sidebar).title) { + let url = resolveURL(modelFor(sidebar).url); + + // checks if the sidebar is loading + if (win.gWebPanelURI == url) { + return true; + } + + // checks if the sidebar loaded already + let ele = sb.contentDocument && sb.contentDocument.getElementById(WEB_PANEL_BROWSER_ID); + if (!ele) { + return false; + } + + if (ele.getAttribute('cachedurl') == url) { + return true; + } + + if (ele && ele.contentWindow && ele.contentWindow.location == url) { + return true; + } + } + + // default + return false; +} +exports.isSidebarShowing = isSidebarShowing; + +function showSidebar(window, sidebar, newURL) { + window = window || getMostRecentBrowserWindow(); + + let { promise, resolve, reject } = defer(); + let model = modelFor(sidebar); + + if (!newURL && isSidebarShowing(window, sidebar)) { + resolve({}); + } + else if (!isPrivateBrowsingSupported && isWindowPrivate(window)) { + reject(Error('You cannot show a sidebar on private windows')); + } + else { + sidebar.once('show', resolve); + + let menuitem = window.document.getElementById(makeID(model.id)); + menuitem.setAttribute('checked', true); + + window.openWebPanel(model.title, resolveURL(newURL || model.url)); + } + + return promise; +} +exports.showSidebar = showSidebar; + + +function hideSidebar(window, sidebar) { + window = window || getMostRecentBrowserWindow(); + + let { promise, resolve, reject } = defer(); + + if (!isSidebarShowing(window, sidebar)) { + reject(Error('The sidebar is already hidden')); + } + else { + sidebar.once('hide', resolve); + + // Below was taken from http://mxr.mozilla.org/mozilla-central/source/browser/base/content/browser.js#4775 + // the code for window.todggleSideBar().. + let { document } = window; + let sidebarEle = document.getElementById('sidebar'); + let sidebarTitle = document.getElementById('sidebar-title'); + let sidebarBox = document.getElementById('sidebar-box'); + let sidebarSplitter = document.getElementById('sidebar-splitter'); + let commandID = sidebarBox.getAttribute('sidebarcommand'); + let sidebarBroadcaster = document.getElementById(commandID); + + sidebarBox.hidden = true; + sidebarSplitter.hidden = true; + + sidebarEle.setAttribute('src', 'about:blank'); + //sidebarEle.docShell.createAboutBlankContentViewer(null); + + sidebarBroadcaster.removeAttribute('checked'); + sidebarBox.setAttribute('sidebarcommand', ''); + sidebarTitle.value = ''; + sidebarBox.hidden = true; + sidebarSplitter.hidden = true; + + // TODO: perhaps this isn't necessary if the window is not most recent? + window.gBrowser.selectedBrowser.focus(); + } + + return promise; +} +exports.hideSidebar = hideSidebar; + +function makeID(id) { + return 'jetpack-sidebar-' + id; +} |