summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/ui/sidebar
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/jetpack/sdk/ui/sidebar')
-rw-r--r--toolkit/jetpack/sdk/ui/sidebar/actions.js10
-rw-r--r--toolkit/jetpack/sdk/ui/sidebar/contract.js27
-rw-r--r--toolkit/jetpack/sdk/ui/sidebar/namespace.js15
-rw-r--r--toolkit/jetpack/sdk/ui/sidebar/utils.js8
-rw-r--r--toolkit/jetpack/sdk/ui/sidebar/view.js214
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;
+}