summaryrefslogtreecommitdiff
path: root/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui')
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/awesomebar.js205
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/findbar/suggestion.js77
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/menuitems.js218
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/actions.js9
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/contract.js30
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/state.js8
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/utils.js31
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/toolbarbutton.js179
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/utils.js28
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/validate.js31
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/warning.js43
-rw-r--r--data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/web-panel.js289
12 files changed, 1148 insertions, 0 deletions
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/awesomebar.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/awesomebar.js
new file mode 100644
index 0000000..641dc14
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/awesomebar.js
@@ -0,0 +1,205 @@
+/* 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 {Cc, Ci, Cu, Cm, components} = require('chrome');
+Cu.import("resource://gre/modules/AddonManager.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+
+var {unload} = require("../addon/unload");
+var {listen} = require("../xul/listen");
+var {watchWindows} = require("window-watcher");
+var {clearTimeout, setTimeout} = require("timer");
+
+let handlers = [];
+
+// public api for adding a keyword and search handler
+// TODO: validate, yo
+exports.AwesomeBarSuggestion = function AwesomeBarSuggestion(options) {
+ var i = handlers.push(options) - 1;
+ var destroyed = false;
+
+ return {
+ destroy: function() {
+ if (destroyed) return;
+ destroyed = true;
+ handlers[i] = null;
+ }
+ };
+};
+
+// Bool check if a given string matches a given handler
+function handlerMatch(query, handler) !!(handler && handler.matches.test(query));
+
+// Get first registered handler that matches a given string
+function getMatchingHandler(query) handlers.filter(function(handler) handlerMatch(query, handler)).shift()
+
+// Add functionality to search from the location bar and hook up autocomplete
+function addAddressBarSearch(window) {
+ let {change} = makeWindowHelpers(window);
+ let {BrowserUI, gBrowser, gURLBar} = window;
+
+ // Check the input to see if the add-on icon should be shown
+ // Called when the location bar fires the input event
+ function onLocationBarInput() {
+ if (skipCheck())
+ return;
+
+ let icon = "";
+ let handler = getMatchingHandler(urlbar.value);
+ if (handler && handler.icon)
+ icon = handler.icon;
+ setIcon(icon);
+ }
+
+ // Implement these functions depending on the platform
+ let setIcon, skipCheck, urlbar;
+
+ // mobile
+ if (gBrowser == null) {
+ setIcon = function(url) BrowserUI._updateIcon(url);
+ skipCheck = function() false;
+ urlbar = BrowserUI._edit;
+
+ // Check the input on various events
+ listen(window, BrowserUI._edit, "input", onLocationBarInput);
+
+ // Convert inputs to twitter urls
+ change(window.Browser, "getShortcutOrURI", function(orig) {
+ return function(uri, data) {
+ return orig.call(this, uri, data);
+ };
+ });
+ }
+ // desktop
+ else {
+ setIcon = function(url) window.PageProxySetIcon(url);
+ skipCheck = function() gURLBar.getAttribute("pageproxystate") == "valid" &&
+ !gURLBar.hasAttribute("focused");
+ urlbar = gURLBar;
+
+ // Check the input on various events
+ listen(window, gURLBar, "input", onLocationBarInput);
+ listen(window, gBrowser.tabContainer, "TabSelect", onLocationBarInput);
+
+ // Convert inputs to twitter urls
+ change(gURLBar, "_canonizeURL", function(orig) {
+ return function(event) {
+ return orig.call(this, event);
+ };
+ });
+ }
+
+ // Provide a way to set the autocomplete search engines and initialize
+ function setSearch(engines) {
+ urlbar.setAttribute("autocompletesearch", engines);
+ urlbar.mSearchNames = null;
+ urlbar.initSearchNames();
+ };
+
+ // Add in the twitter search and remove on cleanup
+ let origSearch = urlbar.getAttribute("autocompletesearch");
+ setSearch(require('self').id + " " + origSearch);
+ unload(function() setSearch(origSearch));
+}
+
+// Add an autocomplete search engine to provide location bar suggestions
+function addAutocomplete() {
+ const contract = "@mozilla.org/autocomplete/search;1?name=" + require('self').id;
+ const desc = "Jetpack Autocomplete";
+ const uuid = components.ID("504A8466-8D3D-11E0-A57E-D2F94824019B");
+
+ // Keep a timer to send a delayed no match
+ let timer;
+ function clearTimer() {
+ if (timer != null)
+ clearTimeout(timer);
+ timer = null;
+ }
+
+ // call back in one second
+ function setTimer(callback) {
+ timer = setTimeout(callback, 1000);
+ }
+
+ // Implement the autocomplete search that handles twitter queries
+ let search = {
+ createInstance: function(outer, iid) search.QueryInterface(iid),
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteSearch]),
+
+ // Handle searches from the location bar
+ startSearch: function(query, param, previous, listener) {
+
+ // Always clear the timer on a new search
+ clearTimer();
+
+ function suggest(o, done) {
+ listener.onSearchResult(search, {
+ getCommentAt: function() o.title,
+ getImageAt: function() o.favicon,
+ getLabelAt: function() o.label || o.url,
+ getValueAt: function() o.url,
+ getStyleAt: function() "favicon",
+ get matchCount() 1,
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult]),
+ removeValueAt: function() {},
+ searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
+ get searchString() query,
+ });
+ }
+
+ // TODO: if no search yet, but matched keyword, show example text
+
+ // if there's a query string and a match
+ if (query.length) {
+ let handler = getMatchingHandler(query);
+ if (handler) {
+ if (query) {
+ handler.onSearch(query, suggest);
+ }
+ }
+ }
+ // Send a delayed NOMATCH so the autocomplete doesn't close early
+ else {
+ setTimer(function() {
+ listener.onSearchResult(search, {
+ searchResult: Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
+ });
+ });
+ }
+ },
+
+ // Nothing to cancel other than a delayed search as results are synchronous
+ stopSearch: function() {
+ clearTimer();
+ },
+ };
+
+ // Register this autocomplete search service and clean up when necessary
+ const registrar = Ci.nsIComponentRegistrar;
+ Cm.QueryInterface(registrar).registerFactory(uuid, desc, contract, search);
+ unload(function() {
+ Cm.QueryInterface(registrar).unregisterFactory(uuid, search);
+ });
+}
+
+// Add support to the browser
+watchWindows(addAddressBarSearch);
+addAutocomplete();
+
+// Take a window and create various helper properties and functions
+function makeWindowHelpers(window) {
+ // Replace a value with another value or a function of the original value
+ function change(obj, prop, val) {
+ let orig = obj[prop];
+ obj[prop] = typeof val == "function" ? val(orig) : val;
+ unload(function() obj[prop] = orig, window);
+ }
+
+ return {
+ change: change,
+ };
+}
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/findbar/suggestion.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/findbar/suggestion.js
new file mode 100644
index 0000000..efbc665
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/findbar/suggestion.js
@@ -0,0 +1,77 @@
+/* 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 winUtils = require('sdk/deprecated/window-utils');
+const { Class } = require('sdk/core/heritage');
+const { validateOptions } = require('sdk/deprecated/api-utils');
+const { isBrowser } = require('sdk/window/utils');
+const { unload } = require('../../addon/unload');
+const { listen } = require('../../xul/listen');
+
+const findsuggestionNS = require('sdk/core/namespace').ns();
+const NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
+
+function FindSuggestionOptions(options) {
+ return validateOptions(options, {
+ word: { is: ['string'] },
+ //onClick: { is: ['undefined', 'function'] }
+ });
+}
+
+const FindSuggestion = Class({
+ initialize: function(options) {
+ options = findsuggestionNS(this).options = FindSuggestionOptions(options);
+ let unloaders = findsuggestionNS(this).unloaders = [];
+
+ winUtils.WindowTracker({
+ onTrack: function(window) {
+ if (!isBrowser(window)) return;
+
+ let findBar = window.gFindBar;
+ let findContainer = findBar.getElement('findbar-container');
+
+ // Show these suggestions in the findbar
+ let ele = window.document.createElementNS(NS_XUL, 'label');
+ ele.setAttribute('value', options.word);
+ ele.style.margin = '2px';
+ ele.style.cursor = 'pointer';
+ ele.style.fontWeight = 'bold';
+ findContainer.appendChild(ele);
+
+ ele.addEventListener('click', suggestionClick.bind({
+ findBar: findBar
+ }), false);
+
+ // Clear out the suggestions when removing the add-on
+ function clearSuggestion() {
+ findContainer.removeChild(ele);
+ }
+
+ // save a destroyer
+ unloaders.push(
+ destroyer.bind(null, unload(clearSuggestion, window), clearSuggestion));
+ }
+ });
+ },
+ destroy: function() findsuggestionNS(this).unloaders.forEach(function(x) x())
+});
+exports.FindSuggestion = FindSuggestion;
+
+function suggestionClick(event) {
+ let suggestion = event.target.value;
+ let findField = this.findBar._findField;
+
+ if (findField.value === suggestion) {
+ this.findBar.onFindAgainCommand(false);
+ }
+ else {
+ findField.value = suggestion;
+ findBar._find();
+ }
+}
+function destroyer(remover, clearSuggestion) {
+ clearSuggestion();
+ remover();
+}
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/menuitems.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/menuitems.js
new file mode 100644
index 0000000..5e34106
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/menuitems.js
@@ -0,0 +1,218 @@
+/* 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 windowUtils = require("sdk/deprecated/window-utils");
+const { Class } = require("sdk/core/heritage");
+const { validateOptions } = require("sdk/deprecated/api-utils");
+const { on, emit, once, off } = require("sdk/event/core");
+const { isBrowser } = require("sdk/window/utils");
+const { EventTarget } = require('sdk/event/target');
+const menuitemNS = require("sdk/core/namespace").ns();
+
+const { unload } = require('../addon/unload');
+
+const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function MenuitemOptions(options) {
+ return validateOptions(options, {
+ id: { is: ['string'] },
+ menuid: { is: ['undefined', 'string'] },
+ insertbefore: { is: ['undefined', 'string', 'object', 'number'] },
+ label: { is: ["string"] },
+ include: { is: ['string', 'undefined'] },
+ disabled: { is: ["undefined", "boolean"], map: function(v) !!v},
+ accesskey: { is: ["undefined", "string"] },
+ key: { is: ["undefined", "string"] },
+ checked: { is: ['undefined', 'boolean'] },
+ className: { is: ["undefined", "string"] },
+ onCommand: { is: ['undefined', 'function'] },
+ useChrome: { map: function(v) !!v }
+ });
+}
+
+let Menuitem = Class({
+ extends: EventTarget,
+ initialize: function(options) {
+ options = menuitemNS(this).options = MenuitemOptions(options);
+ EventTarget.prototype.initialize.call(this, options);
+
+ menuitemNS(this).destroyed = false;
+ menuitemNS(this).unloaders = [];
+ menuitemNS(this).menuitems = addMenuitems(this, options).menuitems;
+ },
+ get id() menuitemNS(this).options.id,
+ get label() menuitemNS(this).options.label,
+ set label(val) updateProperty(this, 'label', val),
+ get checked() menuitemNS(this).options.checked,
+ set checked(val) updateProperty(this, 'checked', !!val),
+ get disabled() menuitemNS(this).options.disabled,
+ set disabled(val) updateProperty(this, 'disabled', !!val),
+ get key() menuitemNS(this).options.key,
+ set key(val) updateProperty(this, 'key', val),
+ clone: function (overwrites) {
+ let opts = Object.clone(menuitemNS(this).options);
+ for (let key in overwrites) {
+ opts[key] = ovrewrites[key];
+ }
+ return Menuitem(opts);
+ },
+ get menuid() menuitemNS(this).options.menuid,
+ set menuid(val) {
+ let options = menuitemNS(this).options;
+ options.menuid = val;
+
+ forEachMI(function(menuitem, i, $) {
+ updateMenuitemParent(menuitem, options, $);
+ });
+ },
+ destroy: function() {
+ if (!menuitemNS(this).destroyed) {
+ menuitemNS(this).destroyed = true;
+ menuitemNS(this).unloaders.forEach(function(u) u());
+ menuitemNS(this).unloaders = null;
+ menuitemNS(this).menuitems = null;
+ }
+ return true;
+ }
+});
+
+function addMenuitems(self, options) {
+ let menuitems = [];
+
+ // setup window tracker
+ windowUtils.WindowTracker({
+ onTrack: function (window) {
+ if (menuitemNS(self).destroyed) return;
+ if (options.include) {
+ if (options.include != window.location) return;
+ }
+ else if (!isBrowser(window)) {
+ return;
+ }
+
+ // add the new menuitem to a menu
+ var menuitem = updateMenuitemAttributes(
+ window.document.createElementNS(NS_XUL, "menuitem"), options);
+ var menuitems_i = menuitems.push(menuitem) - 1;
+
+ // add the menutiem to the ui
+ let added = updateMenuitemParent(menuitem, options, function(id) window.document.getElementById(id));
+
+ menuitem.addEventListener("command", function() {
+ if (!self.disabled)
+ emit(self, 'command', options.useChrome ? window : null);
+ }, true);
+
+ // add unloader
+ let unloader = function unloader() {
+ menuitem.parentNode && menuitem.parentNode.removeChild(menuitem);
+ menuitems[menuitems_i] = null;
+ };
+
+ menuitemNS(self).unloaders.push(function() {
+ remover();
+ unloader();
+ });
+
+ let remover = unload(unloader, window);
+ }
+
+ });
+
+ return { menuitems: menuitems };
+}
+
+function updateMenuitemParent(menuitem, options, $) {
+ // add the menutiem to the ui
+ if (Array.isArray(options.menuid)) {
+ let ids = options.menuid;
+ for (var len = ids.length, i = 0; i < len; i++) {
+ if (tryParent($(ids[i]), menuitem, options.insertbefore))
+ return true;
+ }
+ }
+ else {
+ return tryParent($(options.menuid), menuitem, options.insertbefore);
+ }
+
+ return false;
+}
+
+function updateMenuitemAttributes(menuitem, options) {
+ menuitem.setAttribute("id", options.id);
+ menuitem.setAttribute("label", options.label);
+
+ if (options.accesskey)
+ menuitem.setAttribute("accesskey", options.accesskey);
+
+ if (options.key)
+ menuitem.setAttribute("key", options.key);
+
+ menuitem.setAttribute("disabled", !!options.disabled);
+
+ if (options.image) {
+ menuitem.classList.add("menuitem-iconic");
+ menuitem.style.listStyleImage = "url('" + options.image + "')";
+ }
+
+ if (options.checked)
+ menuitem.setAttribute('checked', options.checked);
+
+ if (options.className)
+ options.className.split(/\s+/).forEach(function(name) menuitem.classList.add(name));
+
+ return menuitem;
+}
+
+function updateProperty(menuitem, key, val) {
+ menuitemNS(menuitem).options[key] = val;
+
+ forEachMI(function(menuitem) {
+ menuitem.setAttribute(key, val);
+ }, menuitem);
+ return val;
+}
+
+function forEachMI(callback, menuitem) {
+ menuitemNS(menuitem).menuitems.forEach(function(mi, i) {
+ if (!mi) return;
+ callback(mi, i, function(id) mi.ownerDocument.getElementById(id));
+ });
+}
+
+function tryParent(parent, menuitem, before) {
+ if (parent) {
+ if (!before) {
+ parent.appendChild(menuitem);
+ return true;
+ }
+
+ parent.insertBefore(menuitem, insertBefore(parent, before));
+ return true;
+ }
+
+ return false;
+}
+
+function insertBefore(parent, insertBefore) {
+ if (typeof insertBefore == "number") {
+ switch (insertBefore) {
+ case MenuitemExport.FIRST_CHILD:
+ return parent.firstChild;
+ }
+ return null;
+ }
+ else if (typeof insertBefore == "string") {
+ return parent.querySelector("#" + insertBefore);
+ }
+ return insertBefore;
+}
+
+function MenuitemExport(options) {
+ return Menuitem(options);
+}
+MenuitemExport.FIRST_CHILD = 1;
+
+exports.Menuitem = MenuitemExport;
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/actions.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/actions.js
new file mode 100644
index 0000000..12076ca
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/actions.js
@@ -0,0 +1,9 @@
+/* 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');
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/contract.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/contract.js
new file mode 100644
index 0000000..244e60e
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/contract.js
@@ -0,0 +1,30 @@
+/* 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('sdk/util/contract');
+const { isValidURI } = require('sdk/url');
+
+let string = { is: ['string'] };
+
+exports.contract = contract({
+ id: {
+ is: [ 'string' ],
+ ok: function (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: function (v) v.length
+ },
+ url: {
+ is: [ 'string' ],
+ ok: function(url) {
+ return isValidURI(url);
+ },
+ map: function(v) v.toString(),
+ msg: 'The option "url" must be a valid URI.'
+ }
+});
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/state.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/state.js
new file mode 100644
index 0000000..05225da
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/state.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/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/utils.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/utils.js
new file mode 100644
index 0000000..4576c5b
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/sidebar/utils.js
@@ -0,0 +1,31 @@
+/* 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 { getMostRecentBrowserWindow } = require('sdk/window/utils');
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function create(window, details) {
+ let { document } = window;
+
+ let menuitem = document.createElementNS(XUL_NS, 'menuitem');
+ menuitem.setAttribute('id', details.id);
+ menuitem.setAttribute('label', details.title);
+ menuitem.setAttribute('checked', 'false');
+ menuitem.setAttribute('sidebarurl', details.sidebarurl);
+ 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;
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/toolbarbutton.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/toolbarbutton.js
new file mode 100644
index 0000000..38bfb2e
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/toolbarbutton.js
@@ -0,0 +1,179 @@
+/* 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 winUtils = require("sdk/deprecated/window-utils");
+const { isBrowser } = require('sdk/window/utils');
+const { Class } = require('sdk/core/heritage');
+const TBB_NS = require('sdk/core/namespace').ns();
+
+const { validate: validateOptions } = require('./validate');
+const { getToolbarButtons, toolbarbuttonExists } = require('./utils');
+const { unload } = require("../addon/unload");
+const { listen } = require("../xul/listen");
+
+const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+exports.ToolbarButton = Class({
+ initialize: function(options) {
+ TBB_NS(this).unloaders = [];
+
+ const self = this;
+ TBB_NS(this).destroyed = false;
+ TBB_NS(this).destroyFuncs = [];
+ let safeOptions = TBB_NS(this).options = validateOptions(options);
+
+ winUtils.WindowTracker({
+ onTrack: function (window) {
+ if (!isBrowser(window) || TBB_NS(self).destroyed)
+ return;
+
+ let doc = window.document;
+ let $ = function(id) doc.getElementById(id);
+
+ // create toolbar button
+ let tbb = doc.createElementNS(NS_XUL, "toolbarbutton");
+ tbb.setAttribute("id", safeOptions.id);
+ tbb.setAttribute("type", "button");
+ if (safeOptions.image)
+ tbb.setAttribute("image", safeOptions.image);
+ tbb.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
+ tbb.setAttribute("label", safeOptions.label);
+ tbb.setAttribute('tooltiptext', safeOptions.tooltiptext);
+ tbb.addEventListener("command", function() {
+ if (safeOptions.onCommand)
+ safeOptions.onCommand({}); // TODO: provide something?
+
+ if (safeOptions.panel) {
+ safeOptions.panel.show(tbb);
+ }
+ }, true);
+
+ // add toolbarbutton to palette
+ ($("navigator-toolbox") || $("mail-toolbox")).palette.appendChild(tbb);
+
+ // find a toolbar to insert the toolbarbutton into
+ if (TBB_NS(self).options.toolbarID) {
+ var tb = $(TBB_NS(self).options.toolbarID);
+ }
+ if (!tb) {
+ var tb = toolbarbuttonExists(doc, safeOptions.id);
+ }
+
+ // found a toolbar to use?
+ if (tb) {
+ let b4;
+
+ // find the toolbarbutton to insert before
+ if (TBB_NS(self).options.insertbefore) {
+ b4 = $(TBB_NS(self).options.insertbefore);
+ }
+ if (!b4) {
+ let currentset = tb.getAttribute("currentset").split(",");
+ let i = currentset.indexOf(safeOptions.id) + 1;
+
+ // was the toolbarbutton id found in the curent set?
+ if (i > 0) {
+ let len = currentset.length;
+ // find a toolbarbutton to the right which actually exists
+ for (; i < len; i++) {
+ b4 = $(currentset[i]);
+ if (b4) break;
+ }
+ }
+ }
+
+ tb.insertItem(safeOptions.id, b4, null, false);
+ }
+
+ var saveTBNodeInfo = function(e) {
+ TBB_NS(self).options.toolbarID = tbb.parentNode.getAttribute("id") || "";
+ TBB_NS(self).options.insertbefore = (tbb.nextSibling || "")
+ && tbb.nextSibling.getAttribute("id").replace(/^wrapper-/i, "");
+ };
+
+ window.addEventListener("aftercustomization", saveTBNodeInfo, false);
+
+ // add unloader to unload+'s queue
+ var unloadFunc = function() {
+ tbb.parentNode.removeChild(tbb);
+ window.removeEventListener("aftercustomization", saveTBNodeInfo, false);
+ };
+ var index = TBB_NS(self).destroyFuncs.push(unloadFunc) - 1;
+ listen(window, window, "unload", function() {
+ TBB_NS(self).destroyFuncs[index] = null;
+ }, false);
+ TBB_NS(self).unloaders.push(unload(unloadFunc, window));
+ }
+ });
+ },
+ destroy: function() {
+ if (TBB_NS(this).destroyed) return;
+ TBB_NS(this).destroyed = true;
+
+ let options = TBB_NS(this).options;
+
+ if (options.panel)
+ options.panel.destroy();
+
+ // run unload functions
+ TBB_NS(this).destroyFuncs.forEach(function(f) f && f());
+ TBB_NS(this).destroyFuncs.length = 0;
+
+ // remove unload functions from unload+'s queue
+ TBB_NS(this).unloaders.forEach(function(f) f());
+ TBB_NS(this).unloaders.length = 0;
+ },
+ moveTo: function(pos) {
+ if (TBB_NS(this).destroyed) return;
+
+ let options = TBB_NS(this).options;
+
+ // record the new position for future windows
+ TBB_NS(this).options.toolbarID = pos.toolbarID;
+ TBB_NS(this).options.insertbefore = pos.insertbefore;
+
+ // change the current position for open windows
+ for each (var window in winUtils.windowIterator()) {
+ if (!isBrowser(window)) continue;
+
+ let $ = function (id) window.document.getElementById(id);
+
+ // if the move isn't being forced and it is already in the window, abort
+ if (!pos.forceMove && $(this.id)) continue;
+
+ var tb = $(TBB_NS(this).options.toolbarID);
+ var b4 = $(TBB_NS(this).options.insertbefore);
+
+ // TODO: if b4 dne, but insertbefore is in currentset, then find toolbar to right
+
+ if (tb) tb.insertItem(this.id, b4, null, false);
+ };
+ },
+ get id() TBB_NS(this).options.id,
+ get label() TBB_NS(this).options.label,
+ set label(value) {
+ TBB_NS(this).options.label = value;
+ getToolbarButtons(function(tbb) {
+ tbb.label = value;
+ }, this.id);
+ return value;
+ },
+ setIcon: function setIcon(options) {
+ let val = TBB_NS(this).options.image = options.image || options.url;
+ getToolbarButtons(function(tbb) {
+ tbb.image = val;
+ }, this.id);
+ return val;
+ },
+ get image() TBB_NS(this).options.image,
+ set image(value) this.setIcon({image: value}),
+ get tooltiptext() TBB_NS(this).options.tooltiptext,
+ set tooltiptext(value) {
+ TBB_NS(this).options.tooltiptext = value;
+ getToolbarButtons(function(tbb) {
+ tbb.setAttribute('tooltiptext', value);
+ }, this.id);
+ }
+});
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/utils.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/utils.js
new file mode 100644
index 0000000..6ad0304
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/utils.js
@@ -0,0 +1,28 @@
+'use strict';
+
+const winUtils = require("sdk/deprecated/window-utils");
+const { isBrowser } = require('sdk/window/utils');
+
+const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+function getToolbarButtons(callback, id) {
+ let buttons = [];
+ for each (var window in winUtils.windowIterator()) {
+ if (!isBrowser(window)) continue;
+ let tbb = window.document.getElementById(id);
+ if (tbb) buttons.push(tbb);
+ }
+ if (callback) buttons.forEach(callback);
+ return buttons;
+}
+exports.getToolbarButtons = getToolbarButtons;
+
+function toolbarbuttonExists(doc, id) {
+ var toolbars = doc.getElementsByTagNameNS(NS_XUL, "toolbar");
+ for (var i = toolbars.length - 1; ~i; i--) {
+ if ((new RegExp("(?:^|,)" + id + "(?:,|$)")).test(toolbars[i].getAttribute("currentset")))
+ return toolbars[i];
+ }
+ return false;
+}
+exports.toolbarbuttonExists = toolbarbuttonExists;
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/validate.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/validate.js
new file mode 100644
index 0000000..3de6d30
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/validate.js
@@ -0,0 +1,31 @@
+'use strict';
+
+const { validateOptions } = require('sdk/deprecated/api-utils');
+const { Panel } = require('sdk/panel');
+
+ const RULES = {
+ image: { is: ["null", "undefined", "string"] },
+ tooltiptext: {
+ is: ["null", "undefined", "string"],
+ defaultValue: ''
+ },
+ id: {
+ is: ["string"],
+ ok: function (v) v.length > 0,
+ msg: 'BAD ID',
+ readonly: true
+ },
+ label: {
+ is: ["string"],
+ ok: function (v) v.length > 0,
+ msg: 'BAD Label'
+ },
+ panel: {
+ is: ["null", "undefined", "object"],
+ ok: function(v) !v || v instanceof Panel
+ },
+ onCommand: {
+ is: ["null", "undefined", "function"],
+ }
+};
+exports.validate = function(o) validateOptions(o, RULES);
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/warning.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/warning.js
new file mode 100644
index 0000000..bf2b449
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/warning.js
@@ -0,0 +1,43 @@
+/* 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 self = require('sdk/self');
+const tabs = require('sdk/tabs');
+const { Class } = require('sdk/core/heritage');
+const { on, emit, once, off } = require('sdk/event/core');
+const { EventTarget } = require('sdk/event/target');
+
+const awNS = require('sdk/core/namespace').ns();
+
+let AddonWarning = Class({
+ extends: EventTarget,
+ initialize: function initialize(options) {
+ EventTarget.prototype.initialize.call(this, options);
+ awNS(this).options = options;
+ },
+ open: function() {
+ let self = this;
+ let options = awNS(self).options;
+
+ tabs.open({
+ url: module.uri.replace(/lib\/addon-warning\.js/, 'data/warning.html'),
+ onReady: function(tab) {
+ let worker = tab.attach({
+ contentScriptFile: module.uri.replace(/lib\/addon-warning\.js/, 'data/warning-mod.js')
+ });
+
+ worker.port.on('cancel', function(data) {
+ emit(self, 'cancel');
+ });
+ worker.port.on('accept', function(data) {
+ emit(self, 'accept');
+ });
+
+ worker.port.emit('load', options);
+ }
+ });
+ }
+});
+exports.AddonWarning = AddonWarning;
diff --git a/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/web-panel.js b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/web-panel.js
new file mode 100644
index 0000000..f61c958
--- /dev/null
+++ b/data/extensions/jid1-KtlZuoiikVfFew@jetpack/node_modules/pathfinder/lib/ui/web-panel.js
@@ -0,0 +1,289 @@
+/* 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': 'experimental',
+ 'engines': {
+ 'Firefox': '*'
+ }
+};
+
+const { Class } = require('sdk/core/heritage');
+const { merge } = require('sdk/util/object');
+const { Disposable } = require('sdk/core/disposable');
+const { off, emit, setListeners } = require('sdk/event/core');
+const { EventTarget } = require('sdk/event/target');
+const { URL } = require('sdk/url');
+const { add, remove, has, clear, iterator } = require('sdk/lang/weak-set');
+const { WindowTracker } = require('sdk/deprecated/window-utils');
+const { isBrowser, getMostRecentBrowserWindow, windows } = require('sdk/window/utils');
+const { ns } = require('sdk/core/namespace');
+const { remove: removeFromArray } = require('sdk/util/array');
+const { Worker: WorkerTrait } = require('sdk/content/worker');
+
+const { create, dispose } = require('./sidebar/utils');
+const { show, hide } = require('./sidebar/actions');
+const { isShowing } = require('./sidebar/state');
+const { contract } = require('./sidebar/contract');
+
+const Worker = WorkerTrait.resolve({
+ _injectInDocument: '__injectInDocument'
+}).compose({
+ get _injectInDocument() false
+});
+
+const sidebarNS = ns();
+
+const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
+
+let sidebars = {};
+let models = new WeakMap();
+let views = new WeakMap();
+
+function viewsFor(sidebar) views.get(sidebar);
+function modelFor(sidebar) models.get(sidebar);
+
+const WebPanel = Class({
+ implements: [ Disposable ],
+ extends: EventTarget,
+ setup: function(options) {
+ let self = this;
+
+ const windowNS = ns();
+
+ let model = merge({}, contract(options));
+
+ models.set(this, model);
+
+ setListeners(this, options);
+
+ let bars = [];
+ sidebarNS(self).tracker = WindowTracker({
+ onTrack: function(window) {
+ if (!isBrowser(window))
+ return;
+
+ let sidebar = window.document.getElementById('sidebar');
+ let sidebarBox = window.document.getElementById('sidebar-box');
+
+ let bar = create(window, {
+ id: makeID(model.id),
+ title: model.title,
+ sidebarurl: model.url
+ });
+ bars.push(bar);
+ windowNS(window).bar = bar;
+
+ bar.addEventListener('command', function() {
+ if (isSidebarShowing(window, self)) {
+ hideSidebar(window, self);
+ return;
+ }
+
+ showSidebar(window, self);
+ }, false);
+
+ function onSidebarLoad() {
+ // check if the sidebar is ready
+ let isReady = sidebar.docShell && sidebar.contentDocument;
+ if (!isReady)
+ return;
+
+ // check if it is a web panel
+ let panelBrowser = sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
+ if (!panelBrowser) {
+ bar.removeAttribute('checked');
+ return;
+ }
+
+ let sbTitle = window.document.getElementById('sidebar-title');
+ function onWebPanelSidebarLoad() {
+ if (panelBrowser.contentWindow.location != model.url ||
+ sbTitle.value != model.title) {
+ return;
+ }
+
+ let worker = windowNS(window).worker = Worker({
+ window: panelBrowser.contentWindow
+ });
+
+ function onWebPanelSidebarUnload() {
+ panelBrowser.removeEventListener('unload', onWebPanelSidebarUnload, true);
+
+ windowNS(window).onWebPanelSidebarLoad = null;
+
+ // uncheck the associated menuitem
+ bar.setAttribute('checked', 'false');
+
+ emit(self, 'hide', null);
+ emit(self, 'detach', worker);
+ }
+ windowNS(window).onWebPanelSidebarUnload = onWebPanelSidebarUnload;
+ panelBrowser.contentWindow.addEventListener('unload', onWebPanelSidebarUnload, false);
+
+ // check the associated menuitem
+ bar.setAttribute('checked', 'true');
+
+ emit(self, 'show', null);
+ emit(self, 'attach', worker);
+ }
+ windowNS(window).onWebPanelSidebarLoad = onWebPanelSidebarLoad;
+ panelBrowser.addEventListener('DOMWindowCreated', onWebPanelSidebarLoad, true);
+ }
+ windowNS(window).onSidebarLoad = onSidebarLoad;
+ sidebar.addEventListener('load', onSidebarLoad, true);
+ },
+ onUntrack: function(window) {
+ if (!isBrowser(window))
+ return;
+
+ let { bar } = windowNS(window);
+ if (bar) {
+ removeFromArray(viewsFor(self), bar);
+ dispose(bar);
+ }
+
+ let sidebar = window.document.getElementById('sidebar');
+ if (!sidebar)
+ return;
+
+ if (windowNS(window).onSidebarLoad) {
+ sidebar.removeEventListener('load', windowNS(window).onSidebarLoad, true)
+ windowNS(window).onSidebarLoad = null;
+ }
+
+ if (windowNS(window).onWebPanelSidebarLoad) {
+ let webPanel = sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
+ webPanel && webPanel.removeEventListener('DOMWindowCreated', windowNS(window).onWebPanelSidebarLoad, true);
+ windowNS(window).onWebPanelSidebarLoad = null;
+ }
+
+ if (windowNS(window).onWebPanelSidebarUnload) {
+ windowNS(window).onWebPanelSidebarUnload();
+ }
+ }
+ });
+
+ views.set(this, bars);
+
+ add(sidebars, this);
+ },
+ get id() modelFor(this).id,
+ get title() modelFor(this).title,
+ get url() modelFor(this).url,
+ show: function() show(this),
+ hide: function() hide(this),
+ dispose: function() {
+ off(this);
+
+ let wins = windows('navigator:browser', { includePrivate: true });
+ for each (let win in wins) {
+ hideSidebar(win, this);
+ }
+
+ remove(sidebars, this);
+
+ // stop tracking windows
+ sidebarNS(this).tracker.unload();
+ sidebarNS(this).tracker = null;
+
+ views.delete(this);
+ }
+});
+exports.WebPanel = WebPanel;
+
+function showSidebar(window, sidebar) {
+ let model = modelFor(sidebar);
+ //let window = window || getMostRecentBrowserWindow();
+
+ window.openWebPanel(model.title, model.url);
+
+ let menuitem = window.document.getElementById(makeID(model.id));
+ menuitem.setAttribute('checked', true);
+}
+show.define(WebPanel, showSidebar.bind(null, null));
+
+function hideSidebar(window, sidebar) {
+ //window = window || getMostRecentBrowserWindow();
+
+ if (!isSidebarShowing(window, sidebar))
+ return;
+
+ // return window.toggleSidebar();
+
+ // 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 sidebar = 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;
+
+ sidebar.setAttribute('src', 'about:blank');
+ //sidebar.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();
+}
+hide.define(WebPanel, hideSidebar.bind(null, null));
+
+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;
+ }
+
+ // checks if the sidebar is loading
+ if (win.gWebPanelURI == modelFor(sidebar).url) {
+ return false;
+ }
+
+ if (sidebarTitle.value == modelFor(sidebar).title) {
+ // checks if the sidebar loaded already
+ let ele = sb.contentDocument && sb.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
+
+ if (ele.getAttribute('cachedurl') == modelFor(sidebar).url) {
+ return true;
+ }
+
+ if (ele && ele.contentWindow && ele.contentWindow.location == modelFor(sidebar).url) {
+ return true;
+ }
+ }
+
+ // default
+ return false;
+}
+isShowing.define(WebPanel, isSidebarShowing.bind(null, null));
+
+function makeID(id) {
+ return 'pathfinder-sidebar-' + id;
+}