Remove undesired system add-ons (all except PDFjs)

This commit is contained in:
wolfbeast 2018-04-22 13:19:49 +02:00 committed by Roy Tam
commit 9fd29a60f7
182 changed files with 0 additions and 11311 deletions

View file

@ -1,189 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const APP_UPDATE_URL_PREF = "app.update.url";
const REPLACE_KEY = "%OS_VERSION%";
const AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_RESULT_CODE";
// The system is not vulnerable to Bug 1296630.
const CPU_NO_BUG1296630 = 1;
// The system is vulnerable to Bug 1296630.
const CPU_YES_BUG1296630 = 2;
// An error occured when checking if the system is vulnerable to Bug 1296630.
const CPU_ERR_BUG1296630 = 3;
// It is unknown whether the system is vulnerable to Bug 1296630 (should never happen).
const CPU_UNKNOWN_BUG1296630 = 4;
const AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_CPU_ERROR_CODE";
const CPU_SUCCESS = 0;
const CPU_REG_OPEN_ERROR = 1;
const CPU_VENDOR_ID_ERROR = 2;
const CPU_ID_ERROR = 4;
const CPU_REV_ERROR = 8;
const AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME = "aushelper.websense_reg_version";
const AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_REG_EXISTS";
const AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID = "AUSHELPER_WEBSENSE_ERROR_CODE";
const WEBSENSE_SUCCESS = 0;
const WEBSENSE_REG_OPEN_ERROR = 1;
const WEBSENSE_REG_READ_ERROR = 2;
const WEBSENSE_ALREADY_MODIFIED = 4;
Cu.import("resource://gre/modules/Services.jsm");
function startup() {
if (Services.appinfo.OS != "WINNT") {
return;
}
const regCPUPath = "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
let wrk;
let cpuErrorCode = CPU_SUCCESS;
try {
wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, regCPUPath, wrk.ACCESS_READ);
} catch (e) {
Cu.reportError("AUSHelper - unable to open registry. Exception: " + e);
cpuErrorCode |= CPU_REG_OPEN_ERROR;
}
// If any of the following values are successfully retrieved and they don't
// match the condition for that value then it is safe to update. Hence why the
// following checks are somewhat convoluted. The possible values for the
// variable set by each check is as follows:
//
// | Match | No Match | Error |
// variable | true | false | null |
let cpuVendorIDMatch = false;
try {
let cpuVendorID = wrk.readStringValue("VendorIdentifier");
if (cpuVendorID.toLowerCase() == "genuineintel") {
cpuVendorIDMatch = true;
}
} catch (e) {
Cu.reportError("AUSHelper - error getting CPU vendor indentifier. Exception: " + e);
cpuVendorIDMatch = null;
cpuErrorCode |= CPU_VENDOR_ID_ERROR;
}
let cpuIDMatch = false;
try {
let cpuID = wrk.readStringValue("Identifier");
if (cpuID.toLowerCase().indexOf("family 6 model 61 stepping 4") != -1) {
cpuIDMatch = true;
}
} catch (e) {
Cu.reportError("AUSHelper - error getting CPU indentifier. Exception: " + e);
cpuIDMatch = null;
cpuErrorCode |= CPU_ID_ERROR;
}
let microCodeVersions = [0xe, 0x11, 0x12, 0x13, 0x16, 0x18, 0x19];
let cpuRevMatch = null;
try {
let keyNames = ["Update Revision", "Update Signature"];
for (let i = 0; i < keyNames.length; ++i) {
try {
let regVal = wrk.readBinaryValue(keyNames[i]);
if (regVal.length == 8) {
let hexVal = [];
// We are only inyterested in the upper 4 bytes and the little endian
// value for it.
for (let j = 4; j < 8; j++) {
let c = regVal.charCodeAt(j).toString(16);
if (c.length == 1) {
c = "0" + c;
}
hexVal.unshift(c);
}
cpuRevMatch = false;
if (microCodeVersions.indexOf(parseInt(hexVal.join(''))) != -1) {
cpuRevMatch = true;
}
break;
}
} catch (e) {
if (i == keyNames.length - 1) {
// The registry key name's value was not successfully queried.
cpuRevMatch = null;
cpuErrorCode |= CPU_REV_ERROR;
}
}
}
wrk.close();
} catch (ex) {
Cu.reportError("AUSHelper - error getting CPU revision. Exception: " + ex);
cpuRevMatch = null;
cpuErrorCode |= CPU_REV_ERROR;
}
let cpuResult = CPU_UNKNOWN_BUG1296630;
let cpuValue = "(unkBug1296630v1)";
// The following uses strict equality checks since the values can be true,
// false, or null.
if (cpuVendorIDMatch === false || cpuIDMatch === false || cpuRevMatch === false) {
// Since one of the values is false then the system won't be affected by
// bug 1296630 according to the conditions set out in bug 1311515.
cpuValue = "(noBug1296630v1)";
cpuResult = CPU_NO_BUG1296630;
} else if (cpuVendorIDMatch === null || cpuIDMatch === null || cpuRevMatch === null) {
// Since one of the values is null we can't say for sure if the system will
// be affected by bug 1296630.
cpuValue = "(errBug1296630v1)";
cpuResult = CPU_ERR_BUG1296630;
} else if (cpuVendorIDMatch === true && cpuIDMatch === true && cpuRevMatch === true) {
// Since all of the values are true we can say that the system will be
// affected by bug 1296630.
cpuValue = "(yesBug1296630v1)";
cpuResult = CPU_YES_BUG1296630;
}
Services.telemetry.getHistogramById(AUSHELPER_CPU_RESULT_CODE_HISTOGRAM_ID).add(cpuResult);
Services.telemetry.getHistogramById(AUSHELPER_CPU_ERROR_CODE_HISTOGRAM_ID).add(cpuErrorCode);
const regWebsensePath = "Websense\\Agent";
let websenseErrorCode = WEBSENSE_SUCCESS;
let websenseVersion = "";
try {
let regModes = [wrk.ACCESS_READ, wrk.ACCESS_READ | wrk.WOW64_64];
for (let i = 0; i < regModes.length; ++i) {
wrk.open(wrk.ROOT_KEY_LOCAL_MACHINE, "SOFTWARE", regModes[i]);
try {
if (wrk.hasChild(regWebsensePath)) {
let childKey = wrk.openChild(regWebsensePath, wrk.ACCESS_READ);
websenseVersion = childKey.readStringValue("InstallVersion");
Services.telemetry.scalarSet(AUSHELPER_WEBSENSE_REG_VERSION_SCALAR_NAME, websenseVersion);
}
wrk.close();
} catch (e) {
Cu.reportError("AUSHelper - unable to read registry. Exception: " + e);
websenseErrorCode |= WEBSENSE_REG_READ_ERROR;
}
}
} catch (ex) {
Cu.reportError("AUSHelper - unable to open registry. Exception: " + ex);
websenseErrorCode |= WEBSENSE_REG_OPEN_ERROR;
}
Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_REG_EXISTS_HISTOGRAM_ID).add(!!websenseVersion);
let websenseValue = "(" + (websenseVersion ? "websense-" + websenseVersion : "nowebsense") + ")";
let branch = Services.prefs.getDefaultBranch("");
let curValue = branch.getCharPref(APP_UPDATE_URL_PREF);
if (curValue.indexOf(REPLACE_KEY + "/") > -1) {
let newValue = curValue.replace(REPLACE_KEY + "/", REPLACE_KEY + cpuValue + websenseValue + "/");
branch.setCharPref(APP_UPDATE_URL_PREF, newValue);
} else {
websenseErrorCode |= WEBSENSE_ALREADY_MODIFIED;
}
Services.telemetry.getHistogramById(AUSHELPER_WEBSENSE_ERROR_CODE_HISTOGRAM_ID).add(websenseErrorCode);
}
function shutdown() {}
function install() {}
function uninstall() {}

View file

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>aushelper@mozilla.org</em:id>
<em:version>2.0</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this extension can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Application Update Service Helper</em:name>
<em:description>Sets value(s) in the update url based on custom checks.</em:description>
</Description>
</RDF>

View file

@ -1,16 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
FINAL_TARGET_FILES.features['aushelper@mozilla.org'] += [
'bootstrap.js'
]
FINAL_TARGET_PP_FILES.features['aushelper@mozilla.org'] += [
'install.rdf.in'
]

View file

@ -1,197 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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 {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
// The amount of people to be part of e10s
const TEST_THRESHOLD = {
"beta" : 0.5, // 50%
"release" : 1.0, // 100%
"esr" : 1.0, // 100%
};
const ADDON_ROLLOUT_POLICY = {
"beta" : "51alladdons", // Any WebExtension or addon except with mpc = false
"release" : "51set1",
"esr" : "esrA", // WebExtensions and Addons with mpc=true
};
if (AppConstants.RELEASE_OR_BETA) {
// Bug 1348576 - e10s is never enabled for non-official release builds
// This is hacky, but the problem it solves is the following:
// the e10s rollout is controlled by the channel name, which
// is the only way to distinguish between Beta and Release.
// However, non-official release builds (like the ones done by distros
// to ship Firefox on their package managers) do not set a value
// for the release channel, which gets them to the default value
// of.. (drumroll) "default".
// But we can't just always configure the same settings for the
// "default" channel because that's also the name that a locally
// built Firefox gets, and e10s is managed in a different way
// there (directly by prefs, on Nightly and Aurora).
TEST_THRESHOLD.default = TEST_THRESHOLD.release;
ADDON_ROLLOUT_POLICY.default = ADDON_ROLLOUT_POLICY.release;
}
const PREF_COHORT_SAMPLE = "e10s.rollout.cohortSample";
const PREF_COHORT_NAME = "e10s.rollout.cohort";
const PREF_E10S_OPTED_IN = "browser.tabs.remote.autostart";
const PREF_E10S_FORCE_ENABLED = "browser.tabs.remote.force-enable";
const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
const PREF_TOGGLE_E10S = "browser.tabs.remote.autostart.2";
const PREF_E10S_ADDON_POLICY = "extensions.e10s.rollout.policy";
const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
function startup() {
// In theory we only need to run this once (on install()), but
// it's better to also run it on every startup. If the user has
// made manual changes to the prefs, this will keep the data
// reported more accurate.
// It's also fine (and preferred) to just do it here on startup
// (instead of observing prefs), because e10s takes a restart
// to take effect, so we keep the data based on how it was when
// the session started.
defineCohort();
}
function install() {
defineCohort();
}
let cohortDefinedOnThisSession = false;
function defineCohort() {
// Avoid running twice when it was called by install() first
if (cohortDefinedOnThisSession) {
return;
}
cohortDefinedOnThisSession = true;
let updateChannel = UpdateUtils.getUpdateChannel(false);
if (!(updateChannel in TEST_THRESHOLD)) {
setCohort("unsupportedChannel");
return;
}
let addonPolicy = "unknown";
if (updateChannel in ADDON_ROLLOUT_POLICY) {
addonPolicy = ADDON_ROLLOUT_POLICY[updateChannel];
Preferences.set(PREF_E10S_ADDON_POLICY, addonPolicy);
// This is also the proper place to set the blocklist pref
// in case it is necessary.
Preferences.set(PREF_E10S_ADDON_BLOCKLIST,
// bug 1185672 - Tab Mix Plus
"{dc572301-7619-498c-a57d-39143191b318};" +
// bug 1332692 - LastPass
"support@lastpass.com;");
} else {
Preferences.reset(PREF_E10S_ADDON_POLICY);
}
let userOptedOut = optedOut();
let userOptedIn = optedIn();
let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
let testGroup = (getUserSample() < TEST_THRESHOLD[updateChannel]);
let hasNonExemptAddon = Preferences.get(PREF_E10S_HAS_NONEXEMPT_ADDON, false);
let temporaryDisqualification = getTemporaryDisqualification();
let cohortPrefix = "";
if (disqualified) {
cohortPrefix = "disqualified-";
} else if (hasNonExemptAddon) {
cohortPrefix = `addons-set${addonPolicy}-`;
}
if (userOptedOut) {
setCohort("optedOut");
} else if (userOptedIn) {
setCohort("optedIn");
} else if (temporaryDisqualification != "") {
// Users who are disqualified by the backend (from multiprocessBlockPolicy)
// can be put into either the test or control groups, because e10s will
// still be denied by the backend, which is useful so that the E10S_STATUS
// telemetry probe can be correctly set.
// For these volatile disqualification reasons, however, we must not try
// to activate e10s because the backend doesn't know about it. E10S_STATUS
// here will be accumulated as "2 - Disabled", which is fine too.
setCohort(`temp-disqualified-${temporaryDisqualification}`);
Preferences.reset(PREF_TOGGLE_E10S);
} else if (testGroup) {
setCohort(`${cohortPrefix}test`);
Preferences.set(PREF_TOGGLE_E10S, true);
} else {
setCohort(`${cohortPrefix}control`);
Preferences.reset(PREF_TOGGLE_E10S);
}
}
function shutdown(data, reason) {
}
function uninstall() {
}
function getUserSample() {
let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
let value = 0.0;
if (typeof(prefValue) == "string") {
value = parseFloat(prefValue, 10);
return value;
}
if (typeof(prefValue) == "number") {
// convert old integer value
value = prefValue / 100;
} else {
value = Math.random();
}
Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
return value;
}
function setCohort(cohortName) {
Preferences.set(PREF_COHORT_NAME, cohortName);
try {
if (Ci.nsICrashReporter) {
Services.appinfo.QueryInterface(Ci.nsICrashReporter).annotateCrashReport("E10SCohort", cohortName);
}
} catch (e) {}
}
function optedIn() {
return Preferences.get(PREF_E10S_OPTED_IN, false) ||
Preferences.get(PREF_E10S_FORCE_ENABLED, false);
}
function optedOut() {
// Users can also opt-out by toggling back the pref to false.
// If they reset the pref instead they might be re-enabled if
// they are still part of the threshold.
return Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
(Preferences.isSet(PREF_TOGGLE_E10S) &&
Preferences.get(PREF_TOGGLE_E10S) == false);
}
/* If this function returns a non-empty string, it
* means that this particular user should be temporarily
* disqualified due to some particular reason.
* If a user shouldn't be disqualified, then an empty
* string must be returned.
*/
function getTemporaryDisqualification() {
return "";
}

View file

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>e10srollout@mozilla.org</em:id>
<em:version>1.10</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this theme can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Multi-process staged rollout</em:name>
<em:description>Staged rollout of Firefox multi-process feature.</em:description>
</Description>
</RDF>

View file

@ -1,16 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
FINAL_TARGET_FILES.features['e10srollout@mozilla.org'] += [
'bootstrap.js'
]
FINAL_TARGET_PP_FILES.features['e10srollout@mozilla.org'] += [
'install.rdf.in'
]

View file

@ -1,297 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Console",
"resource://gre/modules/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Integration",
"resource://gre/modules/Integration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PermissionUI",
"resource:///modules/PermissionUI.jsm");
XPCOMUtils.defineLazyGetter(this, "gFlyWebBundle", function() {
const tns = {
"flyweb-button.label": "FlyWeb",
"flyweb-button.tooltiptext": "Discover nearby FlyWeb services",
"flyweb-items-empty": "There are no FlyWeb services currently nearby"
};
return {
GetStringFromName(name) {
return tns[name];
}
};
});
const FLYWEB_ENABLED_PREF = "dom.flyweb.enabled";
function install(aData, aReason) {}
function uninstall(aData, aReason) {}
function startup(aData, aReason) {
// Observe pref changes and enable/disable as necessary.
Services.prefs.addObserver(FLYWEB_ENABLED_PREF, prefObserver, false);
// Only initialize if pref is enabled.
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
if (enabled) {
FlyWebView.init();
}
}
function shutdown(aData, aReason) {
Services.prefs.removeObserver(FLYWEB_ENABLED_PREF, prefObserver);
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
if (enabled) {
FlyWebView.uninit();
}
}
// use enabled pref as a way for tests (e.g. test_contextmenu.html) to disable
// the addon when running.
function prefObserver(aSubject, aTopic, aData) {
let enabled = Services.prefs.getBoolPref(FLYWEB_ENABLED_PREF);
if (enabled) {
FlyWebView.init();
} else {
FlyWebView.uninit();
}
}
let gDiscoveryManagerInstance;
class DiscoveryManager {
constructor(aWindow) {
this._discoveryManager = new aWindow.FlyWebDiscoveryManager();
}
destroy() {
if (this._id) {
this.stop();
}
this._discoveryManager = null;
}
start(callback) {
if (!this._id) {
this._id = this._discoveryManager.startDiscovery(this);
}
this._callback = callback;
}
stop() {
this._discoveryManager.stopDiscovery(this._id);
this._id = null;
}
pairWith(serviceId, callback) {
this._discoveryManager.pairWithService(serviceId, {
pairingSucceeded(service) {
callback(service);
},
pairingFailed(error) {
console.error("FlyWeb failed to pair with service " + serviceId, error);
}
});
}
onDiscoveredServicesChanged(services) {
if (!this._id || !this._callback) {
return;
}
this._callback(services);
}
}
const FlyWebPermissionPromptIntegration = (base) => ({
__proto__: base,
createPermissionPrompt(type, request) {
if (type != "flyweb-publish-server") {
return super.createPermissionPrompt(...arguments);
}
return {
__proto__: PermissionUI.PermissionPromptForRequestPrototype,
get request() {
return request;
},
get permissionKey() {
return "flyweb-publish-server";
},
get popupOptions() {
return {
learnMoreURL: "https://flyweb.github.io",
popupIconURL: "chrome://flyweb/skin/icon-64.png",
};
},
get notificationID() {
return "flyweb-publish-server";
},
get anchorID() {
const kAnchorID = "flyweb-publish-server-notification-icon";
let chromeDoc = this.browser.ownerDocument;
let anchor = chromeDoc.getElementById(kAnchorID);
if (!anchor) {
let notificationPopupBox =
chromeDoc.getElementById("notification-popup-box");
let notificationIcon = chromeDoc.createElement("image");
notificationIcon.id = kAnchorID;
notificationIcon.setAttribute("src",
"chrome://flyweb/skin/icon-64.png");
notificationIcon.classList.add("notification-anchor-icon");
notificationIcon.setAttribute("role", "button");
notificationIcon.setAttribute("aria-label",
"View the publish-server request");
notificationIcon.style.filter =
"url('chrome://browser/skin/filters.svg#fill')";
notificationIcon.style.fill = "currentcolor";
notificationIcon.style.opacity = "0.4";
notificationPopupBox.appendChild(notificationIcon);
}
return kAnchorID;
},
get message() {
return "Would you like to let this site start a server accessible " +
"to nearby devices and people?";
},
get promptActions() {
return [{
label: "Allow Server",
accessKey: "A",
action: Ci.nsIPermissionManager.ALLOW_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
}, {
label: "Block Server",
accessKey: "B",
action: Ci.nsIPermissionManager.DENY_ACTION,
expireType: Ci.nsIPermissionManager.EXPIRE_SESSION,
}];
},
};
},
});
let FlyWebView = {
init() {
// Create widget and add it to the menu panel.
CustomizableUI.createWidget({
id: "flyweb-button",
type: "view",
viewId: "flyweb-panel",
label: gFlyWebBundle.GetStringFromName("flyweb-button.label"),
tooltiptext: gFlyWebBundle.GetStringFromName("flyweb-button.tooltiptext"),
onBeforeCreated(aDocument) {
let panel = aDocument.createElement("panelview");
panel.id = "flyweb-panel";
panel.setAttribute("class", "PanelUI-subView");
panel.setAttribute("flex", "1");
let label = aDocument.createElement("label");
label.setAttribute("class", "panel-subview-header");
label.setAttribute("value", gFlyWebBundle.GetStringFromName("flyweb-button.label"));
let empty = aDocument.createElement("description");
empty.id = "flyweb-items-empty";
empty.setAttribute("mousethrough", "always");
empty.textContent = gFlyWebBundle.GetStringFromName("flyweb-items-empty");
let items = aDocument.createElement("vbox");
items.id = "flyweb-items";
items.setAttribute("class", "panel-subview-body");
panel.appendChild(label);
panel.appendChild(empty);
panel.appendChild(items);
panel.addEventListener("command", this);
aDocument.getElementById("PanelUI-multiView").appendChild(panel);
this._sheetURI = Services.io.newURI("chrome://flyweb/skin/flyweb.css", null, null);
aDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).loadSheet(this._sheetURI, 1);
},
onDestroyed(aDocument) {
aDocument.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).removeSheet(this._sheetURI, 1);
},
onViewShowing(aEvent) {
let doc = aEvent.target.ownerDocument;
let items = doc.getElementById("flyweb-items");
let empty = doc.getElementById("flyweb-items-empty");
if (!gDiscoveryManagerInstance) {
gDiscoveryManagerInstance = new DiscoveryManager(doc.defaultView);
}
gDiscoveryManagerInstance.start((services) => {
while (items.firstChild) {
items.firstChild.remove();
}
let fragment = doc.createDocumentFragment();
for (let service of services) {
let button = doc.createElement("toolbarbutton");
button.setAttribute("class", "subviewbutton cui-withicon");
button.setAttribute("label", service.displayName);
button.setAttribute("data-service-id", service.serviceId);
fragment.appendChild(button);
}
items.appendChild(fragment);
empty.hidden = services.length > 0;
});
},
onViewHiding(aEvent) {
gDiscoveryManagerInstance.stop();
},
handleEvent(aEvent) {
if (aEvent.type === "command") {
let serviceId = aEvent.target.getAttribute("data-service-id");
gDiscoveryManagerInstance.pairWith(serviceId, (service) => {
aEvent.view.openUILinkIn(service.uiUrl, "tab");
});
}
}
});
Integration.contentPermission
.register(FlyWebPermissionPromptIntegration);
},
uninit() {
CustomizableUI.destroyWidget("flyweb-button");
if (gDiscoveryManagerInstance) {
gDiscoveryManagerInstance.destroy();
gDiscoveryManagerInstance = null;
}
Integration.contentPermission
.unregister(FlyWebPermissionPromptIntegration);
}
};

View file

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>flyweb@mozilla.org</em:id>
<em:version>1.0.0</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this theme can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>FlyWeb</em:name>
<em:description>Discover nearby services in the browser</em:description>
</Description>
</RDF>

View file

@ -1,10 +0,0 @@
# 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/.
[features/flyweb@mozilla.org] chrome.jar:
% skin flyweb classic/1.0 %skin/linux/
% skin flyweb classic/1.0 %skin/osx/ os=Darwin
% skin flyweb classic/1.0 %skin/windows/ os=WINNT
% skin flyweb-shared classic/1.0 %skin/shared/
skin/ (skin/*)

View file

@ -1,18 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
FINAL_TARGET_FILES.features['flyweb@mozilla.org'] += [
'bootstrap.js'
]
FINAL_TARGET_PP_FILES.features['flyweb@mozilla.org'] += [
'install.rdf.in'
]
JAR_MANIFESTS += ['jar.mn']

View file

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
<circle fill="#797C80" cx="32" cy="52" r="6"/>
<g>
<path fill="#797C80" d="M6.894,15.255c-2.254,1.547-4.386,3.304-6.361,5.279L0.18,20.887l3.536,3.536l0.354-0.354
c1.621-1.62,3.363-3.072,5.196-4.369C8.126,18.464,7.296,16.943,6.894,15.255z"/>
<path fill="#797C80" d="M63.465,20.532C55.061,12.128,43.887,7.5,32,7.5c-2.265,0-4.504,0.17-6.703,0.501
c0.822,1.44,1.3,3.1,1.312,4.87C28.382,12.631,30.181,12.5,32,12.5c10.55,0,20.468,4.108,27.928,11.567l0.354,0.354l3.537-3.535
L63.465,20.532z"/>
</g>
<g>
<path fill="#797C80" d="M16.613,10.94c1.103,0,2,0.897,2,2s-0.897,2-2,2s-2-0.897-2-2S15.51,10.94,16.613,10.94 M16.613,6.94
c-3.313,0-6,2.687-6,6s2.687,6,6,6s6-2.687,6-6S19.926,6.94,16.613,6.94L16.613,6.94z"/>
</g>
<g>
<path fill="#797C80" d="M46.492,37.502c-1.853-1.852-4.002-3.292-6.334-4.305c0.031,0.324,0.05,0.652,0.05,0.984
c0,1.477-0.33,2.874-0.906,4.137c1.329,0.712,2.561,1.623,3.657,2.719l0.354,0.354l3.533-3.535L46.492,37.502z"/>
<path fill="#797C80" d="M20.262,35.207c-0.972,0.683-1.9,1.439-2.758,2.297l-0.354,0.354l3.536,3.537l0.354-0.354
c0.35-0.35,0.715-0.679,1.091-0.99C21.118,38.66,20.446,37.007,20.262,35.207z"/>
</g>
<g>
<path fill="#797C80" d="M30.209,32.182c1.102,0,1.999,0.897,1.999,2s-0.896,2-1.999,2c-1.103,0-2-0.897-2-2
S29.106,32.182,30.209,32.182 M30.209,28.182c-3.313,0-6,2.686-6,6c0,3.312,2.687,6,6,6c3.313,0,5.999-2.688,5.999-6
C36.208,30.867,33.522,28.182,30.209,28.182L30.209,28.182z"/>
</g>
<g>
<path fill="#797C80" d="M32.207,23.716c0-1.497,0.34-2.912,0.932-4.188C32.76,19.515,32.381,19.5,32,19.5
c-8.681,0-16.843,3.381-22.981,9.52l-0.354,0.354l3.535,3.535l0.354-0.354C17.748,27.36,24.654,24.5,32,24.5
c0.083,0,0.165,0.005,0.247,0.006C32.227,24.245,32.207,23.982,32.207,23.716z"/>
<path fill="#797C80" d="M54.98,29.018c-0.987-0.987-2.033-1.896-3.119-2.738c-0.447,1.68-1.313,3.188-2.491,4.399
c0.717,0.586,1.409,1.21,2.073,1.874l0.354,0.354l3.537-3.535L54.98,29.018z"/>
</g>
<g>
<path fill="#797C80" d="M42.207,21.716c1.103,0,2,0.897,2,2s-0.897,2-2,2s-2-0.897-2-2S41.104,21.716,42.207,21.716 M42.207,17.716
c-3.313,0-6,2.687-6,6s2.687,6,6,6s6-2.687,6-6S45.521,17.716,42.207,17.716L42.207,17.716z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -1,5 +0,0 @@
/* 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/. */
@import url("chrome://flyweb-shared/skin/flyweb.css");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,5 +0,0 @@
/* 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/. */
@import url("chrome://flyweb-shared/skin/flyweb.css");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -1,54 +0,0 @@
/* 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/. */
#flyweb-panel {
width: 20em;
}
#flyweb-items-empty {
box-sizing: border-box;
color: GrayText;
padding: 10px 20px;
text-align: center;
}
#flyweb-button {
list-style-image: url("chrome://flyweb/skin/icon-16.png");
}
#flyweb-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #flyweb-button {
list-style-image: url("chrome://flyweb/skin/icon-32.png");
}
#flyweb-button[cui-areatype="menu-panel"][panel-multiview-anchor="true"] {
list-style-image: url("chrome://flyweb/skin/icon-32-anchored.png");
}
#flyweb-items > toolbarbutton {
list-style-image: url("chrome://flyweb/skin/icon-16.png");
}
@media (min-resolution: 2dppx) {
#flyweb-button {
list-style-image: url("chrome://flyweb/skin/icon-32.png");
}
#flyweb-button[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #flyweb-button {
list-style-image: url("chrome://flyweb/skin/icon-64.png");
}
#flyweb-button[cui-areatype="menu-panel"][panel-multiview-anchor="true"] {
list-style-image: url("chrome://flyweb/skin/icon-64-anchored.png");
}
#flyweb-items > toolbarbutton {
list-style-image: url("chrome://flyweb/skin/icon-32.png");
}
#flyweb-items > toolbarbutton > .toolbarbutton-icon {
width: 16px;
}
}

View file

@ -1,5 +0,0 @@
/* 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/. */
@import url("chrome://flyweb-shared/skin/flyweb.css");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,474 +0,0 @@
"use strict";
module.exports = { // eslint-disable-line no-undef
"extends": "../../.eslintrc.js",
"globals": {
"Components": true,
"dump": true,
"TextDecoder": false,
"TextEncoder": false,
},
"rules": {
// Rules from the mozilla plugin
"mozilla/balanced-listeners": "error",
"mozilla/no-aArgs": "warn",
"mozilla/no-cpows-in-tests": "warn",
"mozilla/var-only-at-top-level": "warn",
"valid-jsdoc": ["error", {
"prefer": {
"return": "returns",
},
"preferType": {
"Boolean": "boolean",
"Number": "number",
"String": "string",
"bool": "boolean",
},
"requireParamDescription": false,
"requireReturn": false,
"requireReturnDescription": false,
}],
// Braces only needed for multi-line arrow function blocks
// "arrow-body-style": ["error", "as-needed"],
// Require spacing around =>
"arrow-spacing": "error",
// Always require spacing around a single line block
"block-spacing": "warn",
// Forbid spaces inside the square brackets of array literals.
"array-bracket-spacing": ["error", "never"],
// Forbid spaces inside the curly brackets of object literals.
"object-curly-spacing": ["error", "never"],
// No space padding in parentheses
"space-in-parens": ["error", "never"],
// Enforce one true brace style (opening brace on the same line) and avoid
// start and end braces on the same line.
"brace-style": ["error", "1tbs", {"allowSingleLine": true}],
// No space before always a space after a comma
"comma-spacing": ["error", {"before": false, "after": true}],
// Commas at the end of the line not the start
"comma-style": "error",
// Don't require spaces around computed properties
"computed-property-spacing": ["warn", "never"],
// Functions are not required to consistently return something or nothing
"consistent-return": "off",
// Require braces around blocks that start a new line
"curly": ["error", "all"],
// Always require a trailing EOL
"eol-last": "error",
// Require function* name()
"generator-star-spacing": ["error", {"before": false, "after": true}],
// Two space indent
"indent": ["error", 2, {"SwitchCase": 1}],
// Space after colon not before in property declarations
"key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}],
// Require spaces before and after finally, catch, etc.
"keyword-spacing": "error",
// Unix linebreaks
"linebreak-style": ["error", "unix"],
// Always require parenthesis for new calls
"new-parens": "error",
// Use [] instead of Array()
"no-array-constructor": "error",
// No duplicate arguments in function declarations
"no-dupe-args": "error",
// No duplicate keys in object declarations
"no-dupe-keys": "error",
// No duplicate cases in switch statements
"no-duplicate-case": "error",
// If an if block ends with a return no need for an else block
// "no-else-return": "error",
// Disallow empty statements. This will report an error for:
// try { something(); } catch (e) {}
// but will not report it for:
// try { something(); } catch (e) { /* Silencing the error because ...*/ }
// which is a valid use case.
"no-empty": "error",
// No empty character classes in regex
"no-empty-character-class": "error",
// Disallow empty destructuring
"no-empty-pattern": "error",
// No assiging to exception variable
"no-ex-assign": "error",
// No using !! where casting to boolean is already happening
"no-extra-boolean-cast": "warn",
// No double semicolon
"no-extra-semi": "error",
// No overwriting defined functions
"no-func-assign": "error",
// No invalid regular expresions
"no-invalid-regexp": "error",
// No odd whitespace characters
"no-irregular-whitespace": "error",
// No single if block inside an else block
"no-lonely-if": "warn",
// No mixing spaces and tabs in indent
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
// Disallow use of multiple spaces (sometimes used to align const values,
// array or object items, etc.). It's hard to maintain and doesn't add that
// much benefit.
"no-multi-spaces": "warn",
// No reassigning native JS objects
"no-native-reassign": "error",
// No (!foo in bar)
"no-negated-in-lhs": "error",
// Nested ternary statements are confusing
"no-nested-ternary": "error",
// Use {} instead of new Object()
"no-new-object": "error",
// No Math() or JSON()
"no-obj-calls": "error",
// No octal literals
"no-octal": "error",
// No redeclaring variables
"no-redeclare": "error",
// No unnecessary comparisons
"no-self-compare": "error",
// No spaces between function name and parentheses
"no-spaced-func": "warn",
// No trailing whitespace
"no-trailing-spaces": "error",
// Error on newline where a semicolon is needed
"no-unexpected-multiline": "error",
// No unreachable statements
"no-unreachable": "error",
// No expressions where a statement is expected
"no-unused-expressions": "error",
// No declaring variables that are never used
"no-unused-vars": ["error", {"args": "none", "varsIgnorePattern": "^(Cc|Ci|Cr|Cu|EXPORTED_SYMBOLS)$"}],
// No using variables before defined
"no-use-before-define": "error",
// No using with
"no-with": "error",
// Always require semicolon at end of statement
"semi": ["error", "always"],
// Require space before blocks
"space-before-blocks": "error",
// Never use spaces before function parentheses
"space-before-function-paren": ["error", {"anonymous": "never", "named": "never"}],
// Require spaces around operators, except for a|"off".
"space-infix-ops": ["error", {"int32Hint": true}],
// ++ and -- should not need spacing
"space-unary-ops": ["warn", {"nonwords": false}],
// No comparisons to NaN
"use-isnan": "error",
// Only check typeof against valid results
"valid-typeof": "error",
// Disallow using variables outside the blocks they are defined (especially
// since only let and const are used, see "no-var").
"block-scoped-var": "error",
// Allow trailing commas for easy list extension. Having them does not
// impair readability, but also not required either.
"comma-dangle": ["error", "always-multiline"],
// Warn about cyclomatic complexity in functions.
"complexity": "warn",
// Don't warn for inconsistent naming when capturing this (not so important
// with auto-binding fat arrow functions).
// "consistent-this": ["error", "self"],
// Don't require a default case in switch statements. Avoid being forced to
// add a bogus default when you know all possible cases are handled.
"default-case": "off",
// Enforce dots on the next line with property name.
"dot-location": ["error", "property"],
// Encourage the use of dot notation whenever possible.
"dot-notation": "error",
// Allow using == instead of ===, in the interest of landing something since
// the devtools codebase is split on convention here.
"eqeqeq": "off",
// Don't require function expressions to have a name.
// This makes the code more verbose and hard to read. Our engine already
// does a fantastic job assigning a name to the function, which includes
// the enclosing function name, and worst case you have a line number that
// you can just look up.
"func-names": "off",
// Allow use of function declarations and expressions.
"func-style": "off",
// Don't enforce the maximum depth that blocks can be nested. The complexity
// rule is a better rule to check this.
"max-depth": "off",
// Maximum length of a line.
// Disabled because we exceed this in too many places.
"max-len": ["off", 80],
// Maximum depth callbacks can be nested.
"max-nested-callbacks": ["error", 4],
// Don't limit the number of parameters that can be used in a function.
"max-params": "off",
// Don't limit the maximum number of statement allowed in a function. We
// already have the complexity rule that's a better measurement.
"max-statements": "off",
// Don't require a capital letter for constructors, only check if all new
// operators are followed by a capital letter. Don't warn when capitalized
// functions are used without the new operator.
"new-cap": ["off", {"capIsNew": false}],
// Allow use of bitwise operators.
"no-bitwise": "off",
// Disallow use of arguments.caller or arguments.callee.
"no-caller": "error",
// Disallow the catch clause parameter name being the same as a variable in
// the outer scope, to avoid confusion.
"no-catch-shadow": "off",
// Disallow assignment in conditional expressions.
"no-cond-assign": "error",
// Disallow using the console API.
"no-console": "error",
// Allow using constant expressions in conditions like while (true)
"no-constant-condition": "off",
// Allow use of the continue statement.
"no-continue": "off",
// Disallow control characters in regular expressions.
"no-control-regex": "error",
// Disallow use of debugger.
"no-debugger": "error",
// Disallow deletion of variables (deleting properties is fine).
"no-delete-var": "error",
// Allow division operators explicitly at beginning of regular expression.
"no-div-regex": "off",
// Disallow use of eval(). We have other APIs to evaluate code in content.
"no-eval": "error",
// Disallow adding to native types
"no-extend-native": "error",
// Disallow unnecessary function binding.
"no-extra-bind": "error",
// Allow unnecessary parentheses, as they may make the code more readable.
"no-extra-parens": "off",
// Disallow fallthrough of case statements, except if there is a comment.
"no-fallthrough": "error",
// Allow the use of leading or trailing decimal points in numeric literals.
"no-floating-decimal": "off",
// Allow comments inline after code.
"no-inline-comments": "off",
// Disallow use of labels for anything other then loops and switches.
"no-labels": ["error", {"allowLoop": true}],
// Disallow use of multiline strings (use template strings instead).
"no-multi-str": "warn",
// Disallow multiple empty lines.
"no-multiple-empty-lines": ["warn", {"max": 2}],
// Allow reassignment of function parameters.
"no-param-reassign": "off",
// Allow string concatenation with __dirname and __filename (not a node env).
"no-path-concat": "off",
// Allow use of unary operators, ++ and --.
"no-plusplus": "off",
// Allow using process.env (not a node environment).
"no-process-env": "off",
// Allow using process.exit (not a node environment).
"no-process-exit": "off",
// Disallow usage of __proto__ property.
"no-proto": "error",
// Disallow multiple spaces in a regular expression literal.
"no-regex-spaces": "error",
// Allow reserved words being used as object literal keys.
"no-reserved-keys": "off",
// Don't restrict usage of specified node modules (not a node environment).
"no-restricted-modules": "off",
// Disallow use of assignment in return statement. It is preferable for a
// single line of code to have only one easily predictable effect.
"no-return-assign": "error",
// Don't warn about declaration of variables already declared in the outer scope.
"no-shadow": "off",
// Disallow shadowing of names such as arguments.
"no-shadow-restricted-names": "error",
// Allow use of synchronous methods (not a node environment).
"no-sync": "off",
// Allow the use of ternary operators.
"no-ternary": "off",
// Disallow throwing literals (eg. throw "error" instead of
// throw new Error("error")).
"no-throw-literal": "error",
// Disallow use of undeclared variables unless mentioned in a /* global */
// block. Note that globals from head.js are automatically imported in tests
// by the import-headjs-globals rule form the mozilla eslint plugin.
"no-undef": "error",
// Allow dangling underscores in identifiers (for privates).
"no-underscore-dangle": "off",
// Allow use of undefined variable.
"no-undefined": "off",
// Disallow the use of Boolean literals in conditional expressions.
"no-unneeded-ternary": "error",
// We use var-only-at-top-level instead of no-var as we allow top level
// vars.
"no-var": "off",
// Allow using TODO/FIXME comments.
"no-warning-comments": "off",
// Don't require method and property shorthand syntax for object literals.
// We use this in the code a lot, but not consistently, and this seems more
// like something to check at code review time.
"object-shorthand": "off",
// Allow more than one variable declaration per function.
"one-var": "off",
// Disallow padding within blocks.
"padded-blocks": ["warn", "never"],
// Don't require quotes around object literal property names.
"quote-props": "off",
// Double quotes should be used.
"quotes": ["warn", "double", {"avoidEscape": true, "allowTemplateLiterals": true}],
// Require use of the second argument for parseInt().
"radix": "error",
// Enforce spacing after semicolons.
"semi-spacing": ["error", {"before": false, "after": true}],
// Don't require to sort variables within the same declaration block.
// Anyway, one-var is disabled.
"sort-vars": "off",
// Require a space immediately following the // in a line comment.
"spaced-comment": ["error", "always"],
// Require "use strict" to be defined globally in the script.
"strict": ["error", "global"],
// Allow vars to be declared anywhere in the scope.
"vars-on-top": "off",
// Don't require immediate function invocation to be wrapped in parentheses.
"wrap-iife": "off",
// Don't require regex literals to be wrapped in parentheses (which
// supposedly prevent them from being mistaken for division operators).
"wrap-regex": "off",
// Disallow Yoda conditions (where literal value comes first).
"yoda": "error",
// disallow use of eval()-like methods
"no-implied-eval": "error",
// Disallow function or variable declarations in nested blocks
"no-inner-declarations": "error",
// Disallow usage of __iterator__ property
"no-iterator": "error",
// Disallow labels that share a name with a variable
"no-label-var": "error",
// Disallow creating new instances of String, Number, and Boolean
"no-new-wrappers": "error",
},
};

View file

@ -1,12 +0,0 @@
/* 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";
/* exported startup, shutdown, install, uninstall */
function startup() {}
function shutdown() {}
function install() {}
function uninstall() {}

View file

@ -1,134 +0,0 @@
/* 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/. */
/*
* Implements a service used by DOM content to request Form Autofill.
*/
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
/**
* Handles profile autofill for a DOM Form element.
* @param {HTMLFormElement} form Form that need to be auto filled
*/
function FormAutofillHandler(form) {
this.form = form;
this.fieldDetails = [];
}
FormAutofillHandler.prototype = {
/**
* DOM Form element to which this object is attached.
*/
form: null,
/**
* Array of collected data about relevant form fields. Each item is an object
* storing the identifying details of the field and a reference to the
* originally associated element from the form.
*
* The "section", "addressType", "contactType", and "fieldName" values are
* used to identify the exact field when the serializable data is received
* from the backend. There cannot be multiple fields which have
* the same exact combination of these values.
*
* A direct reference to the associated element cannot be sent to the user
* interface because processing may be done in the parent process.
*/
fieldDetails: null,
/**
* Returns information from the form about fields that can be autofilled, and
* populates the fieldDetails array on this object accordingly.
*
* @returns {Array<Object>} Serializable data structure that can be sent to the user
* interface, or null if the operation failed because the constraints
* on the allowed fields were not honored.
*/
collectFormFields() {
let autofillData = [];
for (let element of this.form.elements) {
// Query the interface and exclude elements that cannot be autocompleted.
if (!(element instanceof Ci.nsIDOMHTMLInputElement)) {
continue;
}
// Exclude elements to which no autocomplete field has been assigned.
let info = element.getAutocompleteInfo();
if (!info.fieldName || ["on", "off"].includes(info.fieldName)) {
continue;
}
// Store the association between the field metadata and the element.
if (this.fieldDetails.some(f => f.section == info.section &&
f.addressType == info.addressType &&
f.contactType == info.contactType &&
f.fieldName == info.fieldName)) {
// A field with the same identifier already exists.
return null;
}
let inputFormat = {
section: info.section,
addressType: info.addressType,
contactType: info.contactType,
fieldName: info.fieldName,
};
// Clone the inputFormat for caching the fields and elements together
let formatWithElement = Object.assign({}, inputFormat);
inputFormat.index = autofillData.length;
autofillData.push(inputFormat);
formatWithElement.element = element;
this.fieldDetails.push(formatWithElement);
}
return autofillData;
},
/**
* Processes form fields that can be autofilled, and populates them with the
* data provided by backend.
*
* @param {Array<Object>} autofillResult
* Data returned by the user interface.
* [{
* section: Value originally provided to the user interface.
* addressType: Value originally provided to the user interface.
* contactType: Value originally provided to the user interface.
* fieldName: Value originally provided to the user interface.
* value: String with which the field should be updated.
* index: Index to match the input in fieldDetails
* }],
* }
*/
autofillFormFields(autofillResult) {
for (let field of autofillResult) {
// Get the field details, if it was processed by the user interface.
let fieldDetail = this.fieldDetails[field.index];
// Avoid the invalid value set
if (!fieldDetail || !field.value) {
continue;
}
let info = fieldDetail.element.getAutocompleteInfo();
if (field.section != info.section ||
field.addressType != info.addressType ||
field.contactType != info.contactType ||
field.fieldName != info.fieldName) {
Cu.reportError("Autocomplete tokens mismatched");
continue;
}
fieldDetail.element.setUserInput(field.value);
}
},
};
this.EXPORTED_SYMBOLS = ["FormAutofillHandler"];

View file

@ -1,173 +0,0 @@
/* 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/. */
/*
* Implements a service used to access storage and communicate with content.
*
* A "fields" array is used to communicate with FormAutofillContent. Each item
* represents a single input field in the content page as well as its
* @autocomplete properties. The schema is as below. Please refer to
* FormAutofillContent.jsm for more details.
*
* [
* {
* section,
* addressType,
* contactType,
* fieldName,
* value,
* index
* },
* {
* // ...
* }
* ]
*/
/* exported FormAutofillParent */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileStorage",
"resource://formautofill/ProfileStorage.jsm");
const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
let FormAutofillParent = {
_profileStore: null,
/**
* Initializes ProfileStorage and registers the message handler.
*/
init: function() {
let storePath =
OS.Path.join(OS.Constants.Path.profileDir, PROFILE_JSON_FILE_NAME);
this._profileStore = new ProfileStorage(storePath);
this._profileStore.initialize();
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
mm.addMessageListener("FormAutofill:PopulateFieldValues", this);
},
/**
* Handles the message coming from FormAutofillContent.
*
* @param {string} message.name The name of the message.
* @param {object} message.data The data of the message.
* @param {nsIFrameMessageManager} message.target Caller's message manager.
*/
receiveMessage: function({name, data, target}) {
switch (name) {
case "FormAutofill:PopulateFieldValues":
this._populateFieldValues(data, target);
break;
}
},
/**
* Returns the instance of ProfileStorage. To avoid syncing issues, anyone
* who needs to access the profile should request the instance by this instead
* of creating a new one.
*
* @returns {ProfileStorage}
*/
getProfileStore: function() {
return this._profileStore;
},
/**
* Uninitializes FormAutofillParent. This is for testing only.
*
* @private
*/
_uninit: function() {
if (this._profileStore) {
this._profileStore._saveImmediately();
this._profileStore = null;
}
let mm = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageListenerManager);
mm.removeMessageListener("FormAutofill:PopulateFieldValues", this);
},
/**
* Populates the field values and notifies content to fill in. Exception will
* be thrown if there's no matching profile.
*
* @private
* @param {string} data.guid
* Indicates which profile to populate
* @param {Fields} data.fields
* The "fields" array collected from content.
* @param {nsIFrameMessageManager} target
* Content's message manager.
*/
_populateFieldValues({guid, fields}, target) {
this._profileStore.notifyUsed(guid);
this._fillInFields(this._profileStore.get(guid), fields);
target.sendAsyncMessage("FormAutofill:fillForm", {fields});
},
/**
* Transforms a word with hyphen into camel case.
* (e.g. transforms "address-type" into "addressType".)
*
* @private
* @param {string} str The original string with hyphen.
* @returns {string} The camel-cased output string.
*/
_camelCase(str) {
return str.toLowerCase().replace(/-([a-z])/g, s => s[1].toUpperCase());
},
/**
* Get the corresponding value from the specified profile according to a valid
* @autocomplete field name.
*
* Note that the field name doesn't need to match the property name defined in
* Profile object. This method can transform the raw data to fulfill it. (e.g.
* inputting "country-name" as "fieldName" will get a full name transformed
* from the country code that is recorded in "country" field.)
*
* @private
* @param {Profile} profile The specified profile.
* @param {string} fieldName A valid @autocomplete field name.
* @returns {string} The corresponding value. Returns "undefined" if there's
* no matching field.
*/
_getDataByFieldName(profile, fieldName) {
let key = this._camelCase(fieldName);
// TODO: Transform the raw profile data to fulfill "fieldName" here.
return profile[key];
},
/**
* Fills in the "fields" array by the specified profile.
*
* @private
* @param {Profile} profile The specified profile to fill in.
* @param {Fields} fields The "fields" array collected from content.
*/
_fillInFields(profile, fields) {
for (let field of fields) {
let value = this._getDataByFieldName(profile, field.fieldName);
if (value !== undefined) {
field.value = value;
}
}
},
};
this.EXPORTED_SYMBOLS = ["FormAutofillParent"];

View file

@ -1,251 +0,0 @@
/* 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/. */
/*
* Implements an interface of the storage of Form Autofill.
*
* The data is stored in JSON format, without indentation, using UTF-8 encoding.
* With indentation applied, the file would look like this:
*
* {
* version: 1,
* profiles: [
* {
* guid, // 12 character...
*
* // profile
* organization, // Company
* streetAddress, // (Multiline)
* addressLevel2, // City/Town
* addressLevel1, // Province (Standardized code if possible)
* postalCode,
* country, // ISO 3166
* tel,
* email,
*
* // metadata
* timeCreated, // in ms
* timeLastUsed, // in ms
* timeLastModified, // in ms
* timesUsed
* },
* {
* // ...
* }
* ]
* }
*/
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
const SCHEMA_VERSION = 1;
// Name-related fields will be handled in follow-up bugs due to the complexity.
const VALID_FIELDS = [
"organization",
"streetAddress",
"addressLevel2",
"addressLevel1",
"postalCode",
"country",
"tel",
"email",
];
function ProfileStorage(path) {
this._path = path;
}
ProfileStorage.prototype = {
/**
* Loads the profile data from file to memory.
*
* @returns {Promise}
* @resolves When the operation finished successfully.
* @rejects JavaScript exception.
*/
initialize() {
this._store = new JSONFile({
path: this._path,
dataPostProcessor: this._dataPostProcessor.bind(this),
});
return this._store.load();
},
/**
* Adds a new profile.
*
* @param {Profile} profile
* The new profile for saving.
*/
add(profile) {
this._store.ensureDataReady();
let profileToSave = this._normalizeProfile(profile);
profileToSave.guid = gUUIDGenerator.generateUUID().toString()
.replace(/[{}-]/g, "").substring(0, 12);
// Metadata
let now = Date.now();
profileToSave.timeCreated = now;
profileToSave.timeLastModified = now;
profileToSave.timeLastUsed = 0;
profileToSave.timesUsed = 0;
this._store.data.profiles.push(profileToSave);
this._store.saveSoon();
},
/**
* Update the specified profile.
*
* @param {string} guid
* Indicates which profile to update.
* @param {Profile} profile
* The new profile used to overwrite the old one.
*/
update(guid, profile) {
this._store.ensureDataReady();
let profileFound = this._findByGUID(guid);
if (!profileFound) {
throw new Error("No matching profile.");
}
let profileToUpdate = this._normalizeProfile(profile);
for (let field of VALID_FIELDS) {
if (profileToUpdate[field] !== undefined) {
profileFound[field] = profileToUpdate[field];
} else {
delete profileFound[field];
}
}
profileFound.timeLastModified = Date.now();
this._store.saveSoon();
},
/**
* Notifies the stroage of the use of the specified profile, so we can update
* the metadata accordingly.
*
* @param {string} guid
* Indicates which profile to be notified.
*/
notifyUsed(guid) {
this._store.ensureDataReady();
let profileFound = this._findByGUID(guid);
if (!profileFound) {
throw new Error("No matching profile.");
}
profileFound.timesUsed++;
profileFound.timeLastUsed = Date.now();
this._store.saveSoon();
},
/**
* Removes the specified profile. No error occurs if the profile isn't found.
*
* @param {string} guid
* Indicates which profile to remove.
*/
remove(guid) {
this._store.ensureDataReady();
this._store.data.profiles =
this._store.data.profiles.filter(profile => profile.guid != guid);
this._store.saveSoon();
},
/**
* Returns the profile with the specified GUID.
*
* @param {string} guid
* Indicates which profile to retrieve.
* @returns {Profile}
* A clone of the profile.
*/
get(guid) {
this._store.ensureDataReady();
let profileFound = this._findByGUID(guid);
if (!profileFound) {
throw new Error("No matching profile.");
}
// Profile is cloned to avoid accidental modifications from outside.
return this._clone(profileFound);
},
/**
* Returns all profiles.
*
* @returns {Array.<Profile>}
* An array containing clones of all profiles.
*/
getAll() {
this._store.ensureDataReady();
// Profiles are cloned to avoid accidental modifications from outside.
return this._store.data.profiles.map(this._clone);
},
_clone(profile) {
return Object.assign({}, profile);
},
_findByGUID(guid) {
return this._store.data.profiles.find(profile => profile.guid == guid);
},
_normalizeProfile(profile) {
let result = {};
for (let key in profile) {
if (!VALID_FIELDS.includes(key)) {
throw new Error(`"${key}" is not a valid field.`);
}
if (typeof profile[key] !== "string" &&
typeof profile[key] !== "number") {
throw new Error(`"${key}" contains invalid data type.`);
}
result[key] = profile[key];
}
return result;
},
_dataPostProcessor(data) {
data.version = SCHEMA_VERSION;
if (!data.profiles) {
data.profiles = [];
}
return data;
},
// For test only.
_saveImmediately() {
return this._store._save();
},
};
this.EXPORTED_SYMBOLS = ["ProfileStorage"];

View file

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>formautofill@mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this extension can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Form Autofill</em:name>
<em:description>Autofill forms with saved profiles</em:description>
</Description>
</RDF>

View file

@ -1,7 +0,0 @@
# 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/.
[features/formautofill@mozilla.org] chrome.jar:
% resource formautofill %content/
content/ (content/*)

View file

@ -1,18 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
FINAL_TARGET_FILES.features['formautofill@mozilla.org'] += [
'bootstrap.js'
]
FINAL_TARGET_PP_FILES.features['formautofill@mozilla.org'] += [
'install.rdf.in'
]
JAR_MANIFESTS += ['jar.mn']

View file

@ -5,16 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
# 'aushelper',
# 'e10srollout',
'pdfjs',
# 'pocket',
# 'webcompat',
]
# Only include the following system add-ons if building Aurora or Nightly
if 'a' in CONFIG['GRE_MILESTONE']:
DIRS += [
'flyweb',
'formautofill',
]

View file

@ -1,511 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
const {classes: Cc, interfaces: Ci, utils: Cu, manager: Cm} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Pocket",
"chrome://pocket/content/Pocket.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AboutPocket",
"chrome://pocket/content/AboutPocket.jsm");
XPCOMUtils.defineLazyGetter(this, "gPocketBundle", function() {
return Services.strings.createBundle("chrome://pocket/locale/pocket.properties");
});
XPCOMUtils.defineLazyGetter(this, "gPocketStyleURI", function() {
return Services.io.newURI("chrome://pocket/skin/pocket.css", null, null);
});
// Due to bug 1051238 frame scripts are cached forever, so we can't update them
// as a restartless add-on. The Math.random() is the work around for this.
const PROCESS_SCRIPT = "chrome://pocket/content/pocket-content-process.js?" + Math.random();
const PREF_BRANCH = "extensions.pocket.";
const PREFS = {
enabled: true, // bug 1229937, figure out ui tour support
api: "api.getpocket.com",
site: "getpocket.com",
oAuthConsumerKey: "40249-e88c401e1b1f2242d9e441c4"
};
function setDefaultPrefs() {
let branch = Services.prefs.getDefaultBranch(PREF_BRANCH);
for (let [key, val] of Object.entries(PREFS)) {
// If someone beat us to setting a default, don't overwrite it. This can
// happen if distribution.ini sets the default first.
if (branch.getPrefType(key) != branch.PREF_INVALID)
continue;
switch (typeof val) {
case "boolean":
branch.setBoolPref(key, val);
break;
case "number":
branch.setIntPref(key, val);
break;
case "string":
branch.setCharPref(key, val);
break;
}
}
}
function createElementWithAttrs(document, type, attrs) {
let element = document.createElement(type);
Object.keys(attrs).forEach(function (attr) {
element.setAttribute(attr, attrs[attr]);
})
return element;
}
function CreatePocketWidget(reason) {
let id = "pocket-button"
let widget = CustomizableUI.getWidget(id);
// The widget is only null if we've created then destroyed the widget.
// Once we've actually called createWidget the provider will be set to
// PROVIDER_API.
if (widget && widget.provider == CustomizableUI.PROVIDER_API)
return;
// if upgrading from builtin version and the button was placed in ui,
// seenWidget will not be null
let seenWidget = CustomizableUI.getPlacementOfWidget("pocket-button", false, true);
let pocketButton = {
id: "pocket-button",
defaultArea: CustomizableUI.AREA_NAVBAR,
introducedInVersion: "pref",
type: "view",
tabSpecific: true,
viewId: "PanelUI-pocketView",
label: gPocketBundle.GetStringFromName("pocket-button.label"),
tooltiptext: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
// Use forwarding functions here to avoid loading Pocket.jsm on startup:
onViewShowing: function() {
return Pocket.onPanelViewShowing.apply(this, arguments);
},
onViewHiding: function() {
return Pocket.onPanelViewHiding.apply(this, arguments);
},
onBeforeCreated: function(doc) {
// Bug 1223127,CUI should make this easier to do.
if (doc.getElementById("PanelUI-pocketView"))
return;
let view = doc.createElement("panelview");
view.id = "PanelUI-pocketView";
let panel = doc.createElement("vbox");
panel.setAttribute("class", "panel-subview-body");
view.appendChild(panel);
doc.getElementById("PanelUI-multiView").appendChild(view);
}
};
CustomizableUI.createWidget(pocketButton);
CustomizableUI.addListener(pocketButton);
// placed is null if location is palette
let placed = CustomizableUI.getPlacementOfWidget("pocket-button");
// a first time install will always have placed the button somewhere, and will
// not have a placement prior to creating the widget. Thus, !seenWidget &&
// placed.
if (reason == ADDON_ENABLE && !seenWidget && placed) {
// initially place the button after the bookmarks button if it is in the UI
let widgets = CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR);
let bmbtn = widgets.indexOf("bookmarks-menu-button");
if (bmbtn > -1) {
CustomizableUI.moveWidgetWithinArea("pocket-button", bmbtn + 1);
}
}
// Uninstall the Pocket social provider if it exists, but only if we haven't
// already uninstalled it in this manner. That way the user can reinstall
// it if they prefer it without its being uninstalled every time they start
// the browser.
let SocialService;
try {
// For Firefox 51+
SocialService = Cu.import("resource:///modules/SocialService.jsm", {}).SocialService;
} catch (e) {
SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
}
let origin = "https://getpocket.com";
SocialService.getProvider(origin, provider => {
if (provider) {
let pref = "social.backup.getpocket-com";
if (!Services.prefs.prefHasUserValue(pref)) {
let str = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
str.data = JSON.stringify(provider.manifest);
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, str);
SocialService.uninstallProvider(origin, () => {});
}
}
});
}
// PocketContextMenu
// When the context menu is opened check if we need to build and enable pocket UI.
var PocketContextMenu = {
init: function() {
Services.obs.addObserver(this, "on-build-contextmenu", false);
},
shutdown: function() {
Services.obs.removeObserver(this, "on-build-contextmenu");
// loop through windows and remove context menus
// iterate through all windows and add pocket to them
for (let win of CustomizableUI.windows) {
let document = win.document;
for (let id of ["context-pocket", "context-savelinktopocket"]) {
let element = document.getElementById(id);
if (element)
element.remove();
}
}
},
observe: function(aSubject, aTopic, aData) {
let subject = aSubject.wrappedJSObject;
let document = subject.menu.ownerDocument;
let pocketEnabled = CustomizableUI.getPlacementOfWidget("pocket-button");
let showSaveCurrentPageToPocket = !(subject.onTextInput || subject.onLink ||
subject.isContentSelected || subject.onImage ||
subject.onCanvas || subject.onVideo || subject.onAudio);
let targetUrl = subject.onLink ? subject.linkUrl : subject.pageUrl;
let targetURI = Services.io.newURI(targetUrl, null, null);
let canPocket = pocketEnabled && (targetURI.schemeIs("http") || targetURI.schemeIs("https") ||
(targetURI.schemeIs("about") && ReaderMode.getOriginalUrl(targetUrl)));
let showSaveLinkToPocket = canPocket && !showSaveCurrentPageToPocket && subject.onLink;
// create menu entries if necessary
let menu = document.getElementById("context-pocket");
if (!menu) {
menu = createElementWithAttrs(document, "menuitem", {
"id": "context-pocket",
"label": gPocketBundle.GetStringFromName("saveToPocketCmd.label"),
"accesskey": gPocketBundle.GetStringFromName("saveToPocketCmd.accesskey"),
"oncommand": "Pocket.savePage(gContextMenu.browser, gContextMenu.browser.currentURI.spec, gContextMenu.browser.contentTitle);"
});
let sibling = document.getElementById("context-savepage");
if (sibling.nextSibling) {
sibling.parentNode.insertBefore(menu, sibling.nextSibling);
} else {
sibling.parentNode.appendChild(menu);
}
}
menu.hidden = !(canPocket && showSaveCurrentPageToPocket);
menu = document.getElementById("context-savelinktopocket");
if (!menu) {
menu = createElementWithAttrs(document, "menuitem", {
"id": "context-savelinktopocket",
"label": gPocketBundle.GetStringFromName("saveLinkToPocketCmd.label"),
"accesskey": gPocketBundle.GetStringFromName("saveLinkToPocketCmd.accesskey"),
"oncommand": "Pocket.savePage(gContextMenu.browser, gContextMenu.linkURL);"
});
let sibling = document.getElementById("context-savelink");
if (sibling.nextSibling) {
sibling.parentNode.insertBefore(menu, sibling.nextSibling);
} else {
sibling.parentNode.appendChild(menu);
}
}
menu.hidden = !showSaveLinkToPocket;
}
}
// PocketReader
// Listen for reader mode setup and add our button to the reader toolbar
var PocketReader = {
_hidden: true,
get hidden() {
return this._hidden;
},
set hidden(hide) {
hide = !!hide;
if (hide === this._hidden)
return;
this._hidden = hide;
this.update();
},
startup: function() {
// Setup the listeners, update will be called when the widget is added,
// no need to do that now.
let mm = Services.mm;
mm.addMessageListener("Reader:OnSetup", this);
mm.addMessageListener("Reader:Clicked-pocket-button", this);
},
shutdown: function() {
let mm = Services.mm;
mm.removeMessageListener("Reader:OnSetup", this);
mm.removeMessageListener("Reader:Clicked-pocket-button", this);
this.hidden = true;
},
update: function() {
if (this.hidden) {
Services.mm.broadcastAsyncMessage("Reader:RemoveButton", { id: "pocket-button" });
} else {
Services.mm.broadcastAsyncMessage("Reader:AddButton",
{ id: "pocket-button",
title: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
image: "chrome://pocket/content/panels/img/pocket.svg#pocket-mark" });
}
},
receiveMessage: function(message) {
switch (message.name) {
case "Reader:OnSetup": {
// Tell the reader about our button.
if (this.hidden)
break;
message.target.messageManager.
sendAsyncMessage("Reader:AddButton", { id: "pocket-button",
title: gPocketBundle.GetStringFromName("pocket-button.tooltiptext"),
image: "chrome://pocket/content/panels/img/pocket.svg#pocket-mark"});
break;
}
case "Reader:Clicked-pocket-button": {
let doc = message.target.ownerDocument;
let pocketWidget = doc.getElementById("pocket-button");
let placement = CustomizableUI.getPlacementOfWidget("pocket-button");
if (placement) {
if (placement.area == CustomizableUI.AREA_PANEL) {
doc.defaultView.PanelUI.show().then(function() {
// The DOM node might not exist yet if the panel wasn't opened before.
pocketWidget = doc.getElementById("pocket-button");
pocketWidget.doCommand();
});
} else {
pocketWidget.doCommand();
}
}
break;
}
}
}
}
function pktUIGetter(prop, window) {
return {
get: function() {
// delete any getters for properties loaded from main.js so we only load main.js once
delete window.pktUI;
delete window.pktApi;
delete window.pktUIMessaging;
Services.scriptloader.loadSubScript("chrome://pocket/content/main.js", window);
return window[prop];
},
configurable: true,
enumerable: true
};
}
var PocketOverlay = {
startup: function(reason) {
let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
.getService(Ci.nsIStyleSheetService);
this._sheetType = styleSheetService.AUTHOR_SHEET;
this._cachedSheet = styleSheetService.preloadSheet(gPocketStyleURI,
this._sheetType);
Services.ppmm.loadProcessScript(PROCESS_SCRIPT, true);
PocketReader.startup();
CustomizableUI.addListener(this);
CreatePocketWidget(reason);
PocketContextMenu.init();
for (let win of CustomizableUI.windows) {
this.onWindowOpened(win);
}
},
shutdown: function(reason) {
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
ppmm.broadcastAsyncMessage("PocketShuttingDown");
// Although the ppmm loads the scripts into the chrome process as well,
// we need to manually unregister here anyway to ensure these aren't part
// of the chrome process and avoid errors.
AboutPocket.aboutSaved.unregister();
AboutPocket.aboutSignup.unregister();
CustomizableUI.removeListener(this);
for (let window of CustomizableUI.windows) {
for (let id of ["panelMenu_pocket", "menu_pocket", "BMB_pocket",
"panelMenu_pocketSeparator", "menu_pocketSeparator",
"BMB_pocketSeparator"]) {
let element = window.document.getElementById(id);
if (element)
element.remove();
}
this.removeStyles(window);
// remove script getters/objects
delete window.Pocket;
delete window.pktApi;
delete window.pktUI;
delete window.pktUIMessaging;
}
CustomizableUI.destroyWidget("pocket-button");
PocketContextMenu.shutdown();
PocketReader.shutdown();
},
onWindowOpened: function(window) {
if (window.hasOwnProperty("pktUI"))
return;
this.setWindowScripts(window);
this.addStyles(window);
this.updateWindow(window);
},
setWindowScripts: function(window) {
XPCOMUtils.defineLazyModuleGetter(window, "Pocket",
"chrome://pocket/content/Pocket.jsm");
// Can't use XPCOMUtils for these because the scripts try to define the variables
// on window, and so the defineProperty inside defineLazyGetter fails.
Object.defineProperty(window, "pktApi", pktUIGetter("pktApi", window));
Object.defineProperty(window, "pktUI", pktUIGetter("pktUI", window));
Object.defineProperty(window, "pktUIMessaging", pktUIGetter("pktUIMessaging", window));
},
// called for each window as it is opened
updateWindow: function(window) {
// insert our three menu items
let document = window.document;
let hidden = !CustomizableUI.getPlacementOfWidget("pocket-button");
// add to bookmarksMenu
let sib = document.getElementById("menu_bookmarkThisPage");
if (sib && !document.getElementById("menu_pocket")) {
let menu = createElementWithAttrs(document, "menuitem", {
"id": "menu_pocket",
"label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
"class": "menuitem-iconic", // OSX only
"oncommand": "openUILink(Pocket.listURL, event);",
"hidden": hidden
});
let sep = createElementWithAttrs(document, "menuseparator", {
"id": "menu_pocketSeparator",
"hidden": hidden
});
sib.parentNode.insertBefore(menu, sib);
sib.parentNode.insertBefore(sep, sib);
}
// add to bookmarks-menu-button
sib = document.getElementById("BMB_bookmarksToolbar");
if (sib && !document.getElementById("BMB_pocket")) {
let menu = createElementWithAttrs(document, "menuitem", {
"id": "BMB_pocket",
"label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
"class": "menuitem-iconic bookmark-item subviewbutton",
"oncommand": "openUILink(Pocket.listURL, event);",
"hidden": hidden
});
let sep = createElementWithAttrs(document, "menuseparator", {
"id": "BMB_pocketSeparator",
"hidden": hidden
});
sib.parentNode.insertBefore(menu, sib);
sib.parentNode.insertBefore(sep, sib);
}
// add to PanelUI-bookmarks
sib = document.getElementById("panelMenuBookmarkThisPage");
if (sib && !document.getElementById("panelMenu_pocket")) {
let menu = createElementWithAttrs(document, "toolbarbutton", {
"id": "panelMenu_pocket",
"label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
"class": "subviewbutton cui-withicon",
"oncommand": "openUILink(Pocket.listURL, event);",
"hidden": hidden
});
let sep = createElementWithAttrs(document, "toolbarseparator", {
"id": "panelMenu_pocketSeparator",
"hidden": hidden
});
// nextSibling is no-id toolbarseparator
// insert separator first then button
sib = sib.nextSibling;
sib.parentNode.insertBefore(sep, sib);
sib.parentNode.insertBefore(menu, sib);
}
},
onWidgetAfterDOMChange: function(aWidgetNode) {
if (aWidgetNode.id != "pocket-button") {
return;
}
let doc = aWidgetNode.ownerDocument;
let hidden = !CustomizableUI.getPlacementOfWidget("pocket-button");
for (let prefix of ["panelMenu_", "menu_", "BMB_"]) {
let element = doc.getElementById(prefix + "pocket");
if (element) {
element.hidden = hidden;
doc.getElementById(prefix + "pocketSeparator").hidden = hidden;
}
}
// enable or disable reader button
PocketReader.hidden = hidden;
},
addStyles: function(win) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
utils.addSheet(this._cachedSheet, this._sheetType);
},
removeStyles: function(win) {
let utils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
utils.removeSheet(gPocketStyleURI, this._sheetType);
}
}
// use enabled pref as a way for tests (e.g. test_contextmenu.html) to disable
// the addon when running.
function prefObserver(aSubject, aTopic, aData) {
let enabled = Services.prefs.getBoolPref("extensions.pocket.enabled");
if (enabled)
PocketOverlay.startup(ADDON_ENABLE);
else
PocketOverlay.shutdown(ADDON_DISABLE);
}
function startup(data, reason) {
AddonManager.getAddonByID("isreaditlater@ideashower.com", addon => {
if (addon && addon.isActive)
return;
setDefaultPrefs();
// migrate enabled pref
if (Services.prefs.prefHasUserValue("browser.pocket.enabled")) {
Services.prefs.setBoolPref("extensions.pocket.enabled", Services.prefs.getBoolPref("browser.pocket.enabled"));
Services.prefs.clearUserPref("browser.pocket.enabled");
}
// watch pref change and enable/disable if necessary
Services.prefs.addObserver("extensions.pocket.enabled", prefObserver, false);
if (!Services.prefs.getBoolPref("extensions.pocket.enabled"))
return;
PocketOverlay.startup(reason);
});
}
function shutdown(data, reason) {
// For speed sake, we should only do a shutdown if we're being disabled.
// On an app shutdown, just let it fade away...
if (reason != APP_SHUTDOWN) {
Services.prefs.removeObserver("extensions.pocket.enabled", prefObserver);
PocketOverlay.shutdown(reason);
}
}
function install() {
}
function uninstall() {
}

View file

@ -1,93 +0,0 @@
/* 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 { interfaces: Ci, results: Cr, manager: Cm, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
const PREF_LOG_LEVEL = "loop.debug.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
let consoleOptions = {
maxLogLevelPref: PREF_LOG_LEVEL,
prefix: "Loop"
};
return new ConsoleAPI(consoleOptions);
});
function AboutPage(chromeURL, aboutHost, classID, description, uriFlags) {
this.chromeURL = chromeURL;
this.aboutHost = aboutHost;
this.classID = Components.ID(classID);
this.description = description;
this.uriFlags = uriFlags;
}
AboutPage.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
getURIFlags: function(aURI) { // eslint-disable-line no-unused-vars
return this.uriFlags;
},
newChannel: function(aURI, aLoadInfo) {
let newURI = Services.io.newURI(this.chromeURL, null, null);
let channel = Services.io.newChannelFromURIWithLoadInfo(newURI,
aLoadInfo);
channel.originalURI = aURI;
if (this.uriFlags & Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT) {
let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(aURI);
channel.owner = principal;
}
return channel;
},
createInstance: function(outer, iid) {
if (outer !== null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
},
register: function() {
Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory(
this.classID, this.description,
"@mozilla.org/network/protocol/about;1?what=" + this.aboutHost, this);
},
unregister: function() {
Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory(
this.classID, this);
}
};
/* exported AboutPocket */
var AboutPocket = {};
XPCOMUtils.defineLazyGetter(AboutPocket, "aboutSaved", () =>
new AboutPage("chrome://pocket/content/panels/saved.html",
"pocket-saved",
"{3e759f54-37af-7843-9824-f71b5993ceed}",
"About Pocket Saved",
Ci.nsIAboutModule.ALLOW_SCRIPT |
Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)
);
XPCOMUtils.defineLazyGetter(AboutPocket, "aboutSignup", () =>
new AboutPage("chrome://pocket/content/panels/signup.html",
"pocket-signup",
"{8548329d-00c4-234e-8f17-75026db3b56e}",
"About Pocket Signup",
Ci.nsIAboutModule.ALLOW_SCRIPT |
Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)
);
this.EXPORTED_SYMBOLS = ["AboutPocket"];

View file

@ -1,93 +0,0 @@
/* 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 {classes: Cc, interfaces: Ci, utils: Cu} = Components;
this.EXPORTED_SYMBOLS = ["Pocket"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
"resource:///modules/CustomizableUI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
var Pocket = {
get site() { return Services.prefs.getCharPref("extensions.pocket.site"); },
get listURL() { return "https://" + Pocket.site + "/?src=ff_ext"; },
/**
* Functions related to the Pocket panel UI.
*/
onPanelViewShowing(event) {
let document = event.target.ownerDocument;
let window = document.defaultView;
let iframe = window.pktUI.getPanelFrame();
let urlToSave = Pocket._urlToSave;
let titleToSave = Pocket._titleToSave;
Pocket._urlToSave = null;
Pocket._titleToSave = null;
// ViewShowing fires immediately before it creates the contents,
// in lieu of an AfterViewShowing event, just spin the event loop.
window.setTimeout(function() {
if (urlToSave) {
window.pktUI.tryToSaveUrl(urlToSave, titleToSave);
} else {
window.pktUI.tryToSaveCurrentPage();
}
// pocketPanelDidHide in main.js set iframe to about:blank when it was
// hidden, make sure we're loading the save panel.
if (iframe.contentDocument &&
iframe.contentDocument.readyState == "complete" &&
iframe.contentDocument.documentURI != "about:blank") {
window.pktUI.pocketPanelDidShow();
} else {
// iframe didn't load yet. This seems to always be the case when in
// the toolbar panel, but never the case for a subview.
// XXX this only being fired when it's a _capturing_ listener!
iframe.addEventListener("load", Pocket.onFrameLoaded, true);
}
}, 0);
},
onFrameLoaded(event) {
let document = event.currentTarget.ownerDocument;
let window = document.defaultView;
let iframe = window.pktUI.getPanelFrame();
iframe.removeEventListener("load", Pocket.onFrameLoaded, true);
window.pktUI.pocketPanelDidShow();
},
onPanelViewHiding(event) {
let window = event.target.ownerGlobal;
window.pktUI.pocketPanelDidHide(event);
},
_urlToSave: null,
_titleToSave: null,
savePage(browser, url, title) {
let document = browser.ownerDocument;
let pocketWidget = document.getElementById("pocket-button");
let placement = CustomizableUI.getPlacementOfWidget("pocket-button");
if (!placement)
return;
this._urlToSave = url;
this._titleToSave = title;
if (placement.area == CustomizableUI.AREA_PANEL) {
let win = document.defaultView;
win.PanelUI.show().then(function() {
pocketWidget = document.getElementById("pocket-button");
pocketWidget.doCommand();
});
} else {
pocketWidget.doCommand();
}
},
};

View file

@ -1,737 +0,0 @@
/*
* LICENSE
*
* POCKET MARKS
*
* Notwithstanding the permitted uses of the Software (as defined below) pursuant to the license set forth below, "Pocket," "Read It Later" and the Pocket icon and logos (collectively, the Pocket Marks) are registered and common law trademarks of Read It Later, Inc. This means that, while you have considerable freedom to redistribute and modify the Software, there are tight restrictions on your ability to use the Pocket Marks. This license does not grant you any rights to use the Pocket Marks except as they are embodied in the Software.
*
* ---
*
* SOFTWARE
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Pocket UI module
*
* Handles interactions with Pocket buttons, panels and menus.
*
*/
// TODO : Get the toolbar icons from Firefox's build (Nikki needs to give us a red saved icon)
// TODO : [needs clarificaiton from Fx] Firefox's plan was to hide Pocket from context menus until the user logs in. Now that it's an extension I'm wondering if we still need to do this.
// TODO : [needs clarificaiton from Fx] Reader mode (might be a something they need to do since it's in html, need to investigate their code)
// TODO : [needs clarificaiton from Fx] Move prefs within pktApi.s to sqlite or a local file so it's not editable (and is safer)
// TODO : [nice to have] - Immediately save, buffer the actions in a local queue and send (so it works offline, works like our native extensions)
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ReaderMode",
"resource://gre/modules/ReaderMode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "pktApi",
"chrome://pocket/content/pktApi.jsm");
var pktUI = (function() {
// -- Initialization (on startup and new windows) -- //
var _currentPanelDidShow;
var _currentPanelDidHide;
// Init panel id at 0. The first actual panel id will have the number 1 so
// in case at some point any panel has the id 0 we know there is something
// wrong
var _panelId = 0;
var overflowMenuWidth = 230;
var overflowMenuHeight = 475;
var savePanelWidth = 350;
var savePanelHeights = {collapsed: 153, expanded: 272};
// -- Event Handling -- //
/**
* Event handler when Pocket toolbar button is pressed
*/
function pocketPanelDidShow(event) {
if (_currentPanelDidShow) {
_currentPanelDidShow(event);
}
}
function pocketPanelDidHide(event) {
if (_currentPanelDidHide) {
_currentPanelDidHide(event);
}
// clear the panel
getPanelFrame().setAttribute('src', 'about:blank');
}
// -- Communication to API -- //
/**
* Either save or attempt to log the user in
*/
function tryToSaveCurrentPage() {
tryToSaveUrl(getCurrentUrl(), getCurrentTitle());
}
function tryToSaveUrl(url, title) {
// If the user is logged in, go ahead and save the current page
if (pktApi.isUserLoggedIn()) {
saveAndShowConfirmation(url, title);
return;
}
// If the user is not logged in, show the logged-out state to prompt them to authenticate
showSignUp();
}
// -- Panel UI -- //
/**
* Show the sign-up panel
*/
function showSignUp() {
// AB test: Direct logged-out users to tab vs panel
if (pktApi.getSignupPanelTabTestVariant() == 'v2')
{
let site = Services.prefs.getCharPref("extensions.pocket.site");
openTabWithUrl('https://' + site + '/firefox_learnmore?s=ffi&t=autoredirect&tv=page_learnmore&src=ff_ext', true);
// force the panel closed before it opens
getPanel().hidePopup();
return;
}
// Control: Show panel as normal
getFirefoxAccountSignedInUser(function(userdata)
{
var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
var startheight = 490;
var inOverflowMenu = isInOverflowMenu();
var controlvariant = pktApi.getSignupPanelTabTestVariant() == 'control';
if (inOverflowMenu)
{
startheight = overflowMenuHeight;
}
else
{
startheight = 460;
if (fxasignedin == '1')
{
startheight = 406;
}
}
if (!controlvariant) {
startheight = 427;
}
var variant;
if (inOverflowMenu)
{
variant = 'overflow';
}
else
{
variant = 'storyboard_lm';
}
showPanel("about:pocket-signup?pockethost="
+ Services.prefs.getCharPref("extensions.pocket.site")
+ "&fxasignedin="
+ fxasignedin
+ "&variant="
+ variant
+ '&controlvariant='
+ controlvariant
+ '&inoverflowmenu='
+ inOverflowMenu
+ "&locale="
+ getUILocale(), {
onShow: function() {
},
onHide: panelDidHide,
width: inOverflowMenu ? overflowMenuWidth : 300,
height: startheight
});
});
}
/**
* Show the logged-out state / sign-up panel
*/
function saveAndShowConfirmation(url, title) {
// Validate input parameter
if (typeof url !== 'undefined' && url.startsWith("about:reader?url=")) {
url = ReaderMode.getOriginalUrl(url);
}
var isValidURL = (typeof url !== 'undefined' && (url.startsWith("http") || url.startsWith('https')));
var inOverflowMenu = isInOverflowMenu();
var startheight = pktApi.isPremiumUser() && isValidURL ? savePanelHeights.expanded : savePanelHeights.collapsed;
if (inOverflowMenu) {
startheight = overflowMenuHeight;
}
var panelId = showPanel("about:pocket-saved?pockethost=" + Services.prefs.getCharPref("extensions.pocket.site") + "&premiumStatus=" + (pktApi.isPremiumUser() ? '1' : '0') + '&inoverflowmenu='+inOverflowMenu + "&locale=" + getUILocale(), {
onShow: function() {
var saveLinkMessageId = 'saveLink';
// Send error message for invalid url
if (!isValidURL) {
// TODO: Pass key for localized error in error object
let error = {
message: 'Only links can be saved',
localizedKey: "onlylinkssaved"
};
pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
return;
}
// Check online state
if (!navigator.onLine) {
// TODO: Pass key for localized error in error object
let error = {
message: 'You must be connected to the Internet in order to save to Pocket. Please connect to the Internet and try again.'
};
pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, error);
return;
}
// Add url
var options = {
success: function(data, request) {
var item = data.item;
var successResponse = {
status: "success",
item: item
};
pktUIMessaging.sendMessageToPanel(panelId, saveLinkMessageId, successResponse);
},
error: function(error, request) {
// If user is not authorized show singup page
if (request.status === 401) {
showSignUp();
return;
}
// If there is no error message in the error use a
// complete catch-all
var errorMessage = error.message || "There was an error when trying to save to Pocket.";
var panelError = { message: errorMessage}
// Send error message to panel
pktUIMessaging.sendErrorMessageToPanel(panelId, saveLinkMessageId, panelError);
}
}
// Add title if given
if (typeof title !== "undefined") {
options.title = title;
}
// Send the link
pktApi.addLink(url, options);
},
onHide: panelDidHide,
width: inOverflowMenu ? overflowMenuWidth : savePanelWidth,
height: startheight
});
}
/**
* Open a generic panel
*/
function showPanel(url, options) {
// Add new panel id
_panelId += 1;
url += ("&panelId=" + _panelId);
// We don't have to hide and show the panel again if it's already shown
// as if the user tries to click again on the toolbar button the overlay
// will close instead of the button will be clicked
var iframe = getPanelFrame();
// Register event handlers
registerEventMessages();
// Load the iframe
iframe.setAttribute('src', url);
// Uncomment to leave panel open -- for debugging
// panel.setAttribute('noautohide', true);
// panel.setAttribute('consumeoutsideclicks', false);
//
// For some reason setting onpopupshown and onpopuphidden on the panel directly didn't work, so
// do it this hacky way for now
_currentPanelDidShow = options.onShow;
_currentPanelDidHide = options.onHide;
resizePanel({
width: options.width,
height: options.height
});
return _panelId;
}
/**
* Resize the panel
* options = {
* width: ,
* height: ,
* animate [default false]
* }
*/
function resizePanel(options) {
var iframe = getPanelFrame();
var subview = getSubview();
if (subview) {
// Use the subview's size
iframe.style.width = "100%";
iframe.style.height = subview.parentNode.clientHeight + "px";
} else {
// Set an explicit size, panel will adapt.
iframe.style.width = options.width + "px";
iframe.style.height = options.height + "px";
}
}
/**
* Called when the signup and saved panel was hidden
*/
function panelDidHide() {
// clear the onShow and onHide values
_currentPanelDidShow = null;
_currentPanelDidHide = null;
}
/**
* Register all of the messages needed for the panels
*/
function registerEventMessages() {
var iframe = getPanelFrame();
// Only register the messages once
var didInitAttributeKey = 'did_init';
var didInitMessageListener = iframe.getAttribute(didInitAttributeKey);
if (typeof didInitMessageListener !== "undefined" && didInitMessageListener == 1) {
return;
}
iframe.setAttribute(didInitAttributeKey, 1);
// When the panel is displayed it generated an event called
// "show": we will listen for that event and when it happens,
// send our own "show" event to the panel's script, so the
// script can prepare the panel for display.
var _showMessageId = "show";
pktUIMessaging.addMessageListener(iframe, _showMessageId, function(panelId, data) {
// Let panel know that it is ready
pktUIMessaging.sendMessageToPanel(panelId, _showMessageId);
});
// Open a new tab with a given url and activate if
var _openTabWithUrlMessageId = "openTabWithUrl";
pktUIMessaging.addMessageListener(iframe, _openTabWithUrlMessageId, function(panelId, data, contentPrincipal) {
try {
urlSecurityCheck(data.url, contentPrincipal, Services.scriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
} catch (ex) {
return;
}
// Check if the tab should become active after opening
var activate = true;
if (typeof data.activate !== "undefined") {
activate = data.activate;
}
var url = data.url;
openTabWithUrl(url, activate);
pktUIMessaging.sendResponseMessageToPanel(panelId, _openTabWithUrlMessageId, url);
});
// Close the panel
var _closeMessageId = "close";
pktUIMessaging.addMessageListener(iframe, _closeMessageId, function(panelId, data) {
getPanel().hidePopup();
});
// Send the current url to the panel
var _getCurrentURLMessageId = "getCurrentURL";
pktUIMessaging.addMessageListener(iframe, _getCurrentURLMessageId, function(panelId, data) {
pktUIMessaging.sendResponseMessageToPanel(panelId, _getCurrentURLMessageId, getCurrentUrl());
});
var _resizePanelMessageId = "resizePanel";
pktUIMessaging.addMessageListener(iframe, _resizePanelMessageId, function(panelId, data) {
resizePanel(data);
});
// Callback post initialization to tell background script that panel is "ready" for communication.
pktUIMessaging.addMessageListener(iframe, "listenerReady", function(panelId, data) {
});
pktUIMessaging.addMessageListener(iframe, "collapseSavePanel", function(panelId, data) {
if (!pktApi.isPremiumUser() && !isInOverflowMenu())
resizePanel({width:savePanelWidth, height:savePanelHeights.collapsed});
});
pktUIMessaging.addMessageListener(iframe, "expandSavePanel", function(panelId, data) {
if (!isInOverflowMenu())
resizePanel({width:savePanelWidth, height:savePanelHeights.expanded});
});
// Ask for recently accessed/used tags for auto complete
var _getTagsMessageId = "getTags";
pktUIMessaging.addMessageListener(iframe, _getTagsMessageId, function(panelId, data) {
pktApi.getTags(function(tags, usedTags) {
pktUIMessaging.sendResponseMessageToPanel(panelId, _getTagsMessageId, {
tags: tags,
usedTags: usedTags
});
});
});
// Ask for suggested tags based on passed url
var _getSuggestedTagsMessageId = "getSuggestedTags";
pktUIMessaging.addMessageListener(iframe, _getSuggestedTagsMessageId, function(panelId, data) {
pktApi.getSuggestedTagsForURL(data.url, {
success: function(data, response) {
var suggestedTags = data.suggested_tags;
var successResponse = {
status: "success",
value: {
suggestedTags: suggestedTags
}
}
pktUIMessaging.sendResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, successResponse);
},
error: function(error, response) {
pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _getSuggestedTagsMessageId, error);
}
})
});
// Pass url and array list of tags, add to existing save item accordingly
var _addTagsMessageId = "addTags";
pktUIMessaging.addMessageListener(iframe, _addTagsMessageId, function(panelId, data) {
pktApi.addTagsToURL(data.url, data.tags, {
success: function(data, response) {
var successResponse = {status: "success"};
pktUIMessaging.sendResponseMessageToPanel(panelId, _addTagsMessageId, successResponse);
},
error: function(error, response) {
pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _addTagsMessageId, error);
}
});
});
// Based on clicking "remove page" CTA, and passed unique item id, remove the item
var _deleteItemMessageId = "deleteItem";
pktUIMessaging.addMessageListener(iframe, _deleteItemMessageId, function(panelId, data) {
pktApi.deleteItem(data.itemId, {
success: function(data, response) {
var successResponse = {status: "success"};
pktUIMessaging.sendResponseMessageToPanel(panelId, _deleteItemMessageId, successResponse);
},
error: function(error, response) {
pktUIMessaging.sendErrorResponseMessageToPanel(panelId, _deleteItemMessageId, error);
}
})
});
var _initL10NMessageId = "initL10N";
pktUIMessaging.addMessageListener(iframe, _initL10NMessageId, function(panelId, data) {
var strings = {};
var bundle = Services.strings.createBundle("chrome://pocket/locale/pocket.properties");
var e = bundle.getSimpleEnumeration();
while (e.hasMoreElements()) {
var str = e.getNext().QueryInterface(Components.interfaces.nsIPropertyElement);
if (str.key in data) {
strings[str.key] = bundle.formatStringFromName(str.key, data[str.key], data[str.key].length);
} else {
strings[str.key] = str.value;
}
}
pktUIMessaging.sendResponseMessageToPanel(panelId, _initL10NMessageId, { strings: strings });
});
}
// -- Browser Navigation -- //
/**
* Open a new tab with a given url and notify the iframe panel that it was opened
*/
function openTabWithUrl(url) {
let recentWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (!recentWindow) {
Cu.reportError("Pocket: No open browser windows to openTabWithUrl");
return;
}
// If the user is in permanent private browsing than this is not an issue,
// since the current window will always share the same cookie jar as the other
// windows.
if (!PrivateBrowsingUtils.isWindowPrivate(recentWindow) ||
PrivateBrowsingUtils.permanentPrivateBrowsing) {
recentWindow.openUILinkIn(url, "tab");
return;
}
let windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let win = windows.getNext();
if (!PrivateBrowsingUtils.isWindowPrivate(win)) {
win.openUILinkIn(url, "tab");
return;
}
}
// If there were no non-private windows opened already.
recentWindow.openUILinkIn(url, "window");
}
// -- Helper Functions -- //
function getCurrentUrl() {
return getBrowser().currentURI.spec;
}
function getCurrentTitle() {
return getBrowser().contentTitle;
}
function getPanel() {
var frame = getPanelFrame();
var panel = frame;
while (panel && panel.localName != "panel") {
panel = panel.parentNode;
}
return panel;
}
function getPanelFrame() {
var frame = document.getElementById('pocket-panel-iframe');
if (!frame) {
var frameParent = document.getElementById("PanelUI-pocketView").firstChild;
frame = document.createElement("iframe");
frame.id = 'pocket-panel-iframe';
frame.setAttribute("type", "content");
frameParent.appendChild(frame);
}
return frame;
}
function getSubview() {
var view = document.getElementById("PanelUI-pocketView");
if (view && view.getAttribute("current") == "true")
return view;
return null;
}
function isInOverflowMenu() {
var subview = getSubview();
return !!subview;
}
function getFirefoxAccountSignedInUser(callback) {
fxAccounts.getSignedInUser().then(userData => {
callback(userData);
}).then(null, error => {
callback();
});
}
function getUILocale() {
var locale = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIXULChromeRegistry).
getSelectedLocale("browser");
return locale;
}
/**
* Public functions
*/
return {
getPanelFrame: getPanelFrame,
openTabWithUrl: openTabWithUrl,
pocketPanelDidShow: pocketPanelDidShow,
pocketPanelDidHide: pocketPanelDidHide,
tryToSaveUrl: tryToSaveUrl,
tryToSaveCurrentPage: tryToSaveCurrentPage
};
}());
// -- Communication to Background -- //
// https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
var pktUIMessaging = (function() {
/**
* Prefix message id for message listening
*/
function prefixedMessageId(messageId) {
return 'PKT_' + messageId;
}
/**
* Register a listener and callback for a specific messageId
*/
function addMessageListener(iframe, messageId, callback) {
iframe.addEventListener(prefixedMessageId(messageId), function(e) {
var nodePrincipal = e.target.nodePrincipal;
// ignore to ensure we do not pick up other events in the browser
if (!nodePrincipal || !nodePrincipal.URI || !nodePrincipal.URI.spec.startsWith("about:pocket")) {
return;
}
// Pass in information to callback
var payload = JSON.parse(e.target.getAttribute("payload"))[0];
var panelId = payload.panelId;
var data = payload.data;
callback(panelId, data, nodePrincipal);
// Cleanup the element
e.target.parentNode.removeChild(e.target);
}, false, true);
}
/**
* Send a message to the panel's iframe
*/
function sendMessageToPanel(panelId, messageId, payload) {
if (!isPanelIdValid(panelId)) { return; }
var panelFrame = pktUI.getPanelFrame();
if (!isPocketPanelFrameValid(panelFrame)) { return; }
var doc = panelFrame.contentWindow.document;
var documentElement = doc.documentElement;
// Send message to panel
var panelMessageId = prefixedMessageId(panelId + '_' + messageId);
var AnswerEvt = doc.createElement("PKTMessage");
AnswerEvt.setAttribute("payload", JSON.stringify([payload]));
documentElement.appendChild(AnswerEvt);
var event = doc.createEvent("HTMLEvents");
event.initEvent(panelMessageId, true, false);
AnswerEvt.dispatchEvent(event);
}
function sendResponseMessageToPanel(panelId, messageId, payload) {
var responseMessageId = messageId + "Response";
sendMessageToPanel(panelId, responseMessageId, payload);
}
/**
* Helper function to package an error object and send it to the panel
* iframe as a message response
*/
function sendErrorMessageToPanel(panelId, messageId, error) {
var errorResponse = {status: "error", error: error};
sendMessageToPanel(panelId, messageId, errorResponse);
}
function sendErrorResponseMessageToPanel(panelId, messageId, error) {
var errorResponse = {status: "error", error: error};
sendResponseMessageToPanel(panelId, messageId, errorResponse);
}
/**
* Validation
*/
function isPanelIdValid(panelId) {
// First check if panelId has a valid value > 0. We set the panelId to
// 0 to start. But if for some reason the message is attempted to be
// sent before the panel has a panelId, then it's going to send out
// a message with panelId 0, which is never going to be heard. If this
// happens, it means some race condition occurred where the panel was
// trying to communicate before it should.
if (panelId === 0) {
console.warn("Tried to send message to panel with id 0.")
return false;
}
return true
}
function isPocketPanelFrameValid(panelFrame) {
// Check if panel is available if not throw a warning and bailout.
// We likely try to send to a panel that is not visible anymore
if (typeof panelFrame === "undefined") {
console.warn("Pocket panel frame is undefined");
return false;
}
var contentWindow = panelFrame.contentWindow;
if (typeof contentWindow == "undefined") {
console.warn("Pocket panel frame content window is undefined");
return false;
}
var doc = contentWindow.document;
if (typeof doc === "undefined") {
console.warn("Pocket panel frame content window document is undefined");
return false;
}
var documentElement = doc.documentElement;
if (typeof documentElement === "undefined") {
console.warn("Pocket panel frame content window document document element is undefined");
return false;
}
return true;
}
/**
* Public
*/
return {
addMessageListener: addMessageListener,
sendMessageToPanel: sendMessageToPanel,
sendResponseMessageToPanel: sendResponseMessageToPanel,
sendErrorMessageToPanel: sendErrorMessageToPanel,
sendErrorResponseMessageToPanel: sendErrorResponseMessageToPanel
}
}());

View file

@ -1,6 +0,0 @@
@font-face {
font-family: 'FiraSans';
src: url('../fonts/FiraSans-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}

View file

@ -1,424 +0,0 @@
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
box-sizing: content-box;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
/* Normalization for FF panel defauts
========================================================================== */
html {
outline: none;
padding: 0;
}
a {
color: #0095dd;
margin: 0;
outline: none;
padding: 0;
text-decoration: none;
}
a:hover,
a:active {
color: #008acb;
text-decoration: underline;
}
a:active {
color: #006b9d;
}

View file

@ -1,825 +0,0 @@
/* saved.css
*
* Description:
* With base elements out of the way, this sets all custom styling for the page saved dialog.
*
* Contents:
* Global
* Loading spinner
* Core detail
* Tag entry
* Recent/suggested tags
* Premium upsell
* Token input/autocomplete
* Overflow mode
* Language overrides
*/
/*=Global
--------------------------------------------------------------------------------------- */
.pkt_ext_containersaved {
background-color: #fbfbfb;
border-radius: 4px;
display: block;
font-size: 16px;
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
padding: 0;
position: relative;
text-align: center;
}
.pkt_ext_cf:after {
content: " ";
display:table;
clear:both;
}
.pkt_ext_containersaved .pkt_ext_tag_detail,
.pkt_ext_containersaved .pkt_ext_recenttag_detail,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail {
margin: 0 auto;
padding: 0.25em 1em;
position: relative;
width: auto;
}
/*=Loading spinner
--------------------------------------------------------------------------------------- */
@keyframes pkt_ext_spin {
to {
transform: rotate(1turn);
}
}
.pkt_ext_containersaved {
font-size: 16px;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner {
position: relative;
display: inline-block;
height: 2.5em;
left: 50%;
margin: 2em 0 0 -1.25em;
font-size: 10px;
text-indent: 999em;
position: absolute;
top: 4em;
overflow: hidden;
width: 2.5em;
animation: pkt_ext_spin 0.7s infinite steps(8);
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:before,
.pkt_ext_containersaved .pkt_ext_loadingspinner:after,
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:before,
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:after {
content: '';
position: absolute;
top: 0;
left: 1.125em;
width: 0.25em;
height: 0.75em;
border-radius: .2em;
background: #eee;
box-shadow: 0 1.75em #eee;
transform-origin: 50% 1.25em;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:before {
background: #555;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner:after {
transform: rotate(-45deg);
background: #777;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:before {
transform: rotate(-90deg);
background: #999;
}
.pkt_ext_containersaved .pkt_ext_loadingspinner > div:after {
transform: rotate(-135deg);
background: #bbb;
}
/*=Core detail
--------------------------------------------------------------------------------------- */
.pkt_ext_containersaved .pkt_ext_initload {
left: 0;
position: absolute;
top: 0;
width: 100%;
}
.pkt_ext_containersaved .pkt_ext_detail {
max-height: 0;
opacity: 0;
position: relative;
z-index: 10;
}
.pkt_ext_container_detailactive .pkt_ext_initload {
opacity: 0;
}
.pkt_ext_container_detailactive .pkt_ext_initload .pkt_ext_loadingspinner,
.pkt_ext_container_finalstate .pkt_ext_initload .pkt_ext_loadingspinner {
animation: none;
}
.pkt_ext_container_detailactive .pkt_ext_detail {
max-height: 20em;
opacity: 1;
}
.pkt_ext_container_finalstate .pkt_ext_edit_msg,
.pkt_ext_container_finalstate .pkt_ext_tag_detail,
.pkt_ext_container_finalstate .pkt_ext_suggestedtag_detail,
.pkt_ext_container_finalstate .pkt_ext_item_actions {
opacity: 0;
transition: opacity 0.2s ease-out;
}
.pkt_ext_container_finalerrorstate .pkt_ext_edit_msg,
.pkt_ext_container_finalerrorstate .pkt_ext_tag_detail,
.pkt_ext_container_finalerrorstate .pkt_ext_suggestedtag_detail,
.pkt_ext_container_finalerrorstate .pkt_ext_item_actions {
display: none;
transition: none;
}
.pkt_ext_containersaved h2 {
background: transparent;
border: none;
color: #333;
display: block;
float: none;
font-size: 18px;
font-weight: normal;
letter-spacing: normal;
line-height: 1;
margin: 19px 0 4px;
padding: 0;
position: relative;
text-align: left;
text-transform: none;
}
@keyframes fade_in_out {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.pkt_ext_container_finalstate h2 {
animation: fade_in_out 0.4s ease-out;
}
.pkt_ext_container_finalerrorstate h2 {
animation: none;
color: #d74345;
}
.pkt_ext_containersaved .pkt_ext_errordetail {
display: none;
font-size: 12px;
font-weight: normal;
left: 6.4em;
max-width: 21em;
opacity: 0;
position: absolute;
top: 2.7em;
text-align: left;
visibility: hidden;
}
.pkt_ext_container_finalerrorstate .pkt_ext_errordetail {
display: block;
opacity: 1;
visibility: visible;
}
.pkt_ext_containersaved .pkt_ext_logo {
background: url(../img/pocketlogosolo@1x.png) center center no-repeat;
display: block;
float: left;
height: 40px;
padding: 1.25em 1em;
position: relative;
width: 44px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .pkt_ext_logo {
background-image: url(../img/pocketlogosolo@2x.png);
background-size: 44px 40px;
}
}
.pkt_ext_container_finalerrorstate .pkt_ext_logo {
background-image: url(../img/pocketerror@1x.png);
height: 44px;
width: 44px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_container_finalerrorstate .pkt_ext_logo {
background-image: url(../img/pocketerror@2x.png);
background-size: 44px 44px;
}
}
.pkt_ext_containersaved .pkt_ext_topdetail {
float: left;
}
.pkt_ext_containersaved .pkt_ext_edit_msg {
box-sizing: border-box;
display: none;
font-size: 0.75em;
left: auto;
padding: 0 1.4em;
position: absolute;
text-align: left;
top: 8.7em;
width: 100%;
}
.pkt_ext_containersaved .pkt_ext_edit_msg_error {
color: #d74345;
}
.pkt_ext_containersaved .pkt_ext_edit_msg_active {
display: block;
}
.pkt_ext_containersaved .pkt_ext_item_actions {
background: transparent;
float: none;
height: auto;
margin-bottom: 1em;
margin-top: 0;
width: auto;
}
.pkt_ext_containersaved .pkt_ext_item_actions_disabled {
opacity: 0.5;
}
.pkt_ext_container_finalstate .pkt_ext_item_actions_disabled {
opacity: 0;
}
.pkt_ext_containersaved .pkt_ext_item_actions ul {
background: none;
display: block;
float: none;
font-size: 16px;
height: auto;
margin: 0;
padding: 0;
width: 100%;
}
.pkt_ext_containersaved .pkt_ext_item_actions li {
box-sizing: border-box;
background: none;
border: 0;
float: left;
list-style: none;
line-height: 0.8;
height: auto;
padding-right: 0.4em;
width: auto;
}
.pkt_ext_containersaved .pkt_ext_item_actions li:before {
content: none;
}
.pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_actions_separator {
border-left: 2px solid #777;
height: 0.75em;
margin-top: 0.3em;
padding: 0;
width: 10px;
}
.pkt_ext_containersaved .pkt_ext_item_actions a {
background: transparent;
color: #0095dd;
display: block;
font-feature-settings: normal;
font-size: 12px;
font-weight: normal;
letter-spacing: normal;
line-height: inherit;
height: auto;
margin: 0;
padding: 0.5em;
float: left;
text-align: left;
text-decoration: none;
text-transform: none;
}
.pkt_ext_containersaved .pkt_ext_item_actions a:hover {
color: #008acb;
text-decoration: underline;
}
.pkt_ext_containersaved .pkt_ext_item_actions a:before,
.pkt_ext_containersaved .pkt_ext_item_actions a:after {
background: transparent;
display: none;
}
.pkt_ext_containersaved .pkt_ext_item_actions_disabled a {
cursor: default;
}
.pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_openpocket {
float: right;
padding-right: 0.7em;
text-align: right;
}
.pkt_ext_containersaved .pkt_ext_item_actions .pkt_ext_removeitem {
padding-left: 0;
}
.pkt_ext_containersaved .pkt_ext_close {
background: url(../img/tag_close@1x.png) center center no-repeat;
color: #333;
display: block;
font-size: 0.8em;
height: 10px;
right: 0.5em;
overflow: hidden;
position: absolute;
text-align: center;
text-indent: -9999px;
top: -1em;
width: 10px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .pkt_ext_close {
background-image: url(../img/tag_close@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .pkt_ext_close:hover {
color: #000;
text-decoration: none;
}
/*=Tag entry
--------------------------------------------------------------------------------------- */
.pkt_ext_containersaved .pkt_ext_tag_detail {
border: 1px solid #c1c1c1;
border-radius: 2px;
font-size: 16px;
clear: both;
margin: 1.25em 1em;
padding: 0;
display: flex;
}
.pkt_ext_containersaved .pkt_ext_tag_error {
border: none;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper {
box-sizing: border-box;
flex: 1;
background-color: #fff;
border-right: 1px solid #c3c3c3;
color: #333;
display: block;
float: none;
font-size: 0.875em;
list-style: none;
margin: 0;
overflow: hidden;
padding: 0.25em 0.5em;
width: 14em;
padding-left: 0.5em;
padding-right: 0.5em;
}
.pkt_ext_containersaved .pkt_ext_tag_error .pkt_ext_tag_input_wrapper {
border: 1px solid #d74345;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper .token-input-list {
display: block;
left: 0;
height: 1.7em;
overflow: hidden;
position: relative;
width: 60em;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper .token-input-list,
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper li {
font-size: 14px;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper li {
height: auto;
width: auto;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper li:before {
content: none;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input {
border: 0;
box-shadow: none;
background-color: #fff;
color: #333;
font-size: 14px;
float: left;
line-height: normal;
height: auto;
min-height: 0;
min-width: 5em;
padding: 3px 2px 1px;
text-transform: none;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper input::placeholder {
color: #a9a9a9;
letter-spacing: normal;
text-transform: none;
}
.pkt_ext_containersaved .input_disabled {
cursor: default;
opacity: 0.5;
}
.pkt_ext_containersaved .pkt_ext_btn {
box-sizing: border-box;
color: #333;
float: none;
font-size: 0.875em;
font-size: 14px;
letter-spacing: normal;
height: 2.2em;
min-width: 4em;
padding: 0.5em 0;
text-decoration: none;
text-transform: none;
width: auto;
}
.pkt_ext_containersaved .pkt_ext_btn:hover {
background-color: #ebebeb;
}
.pkt_ext_containersaved .pkt_ext_btn:active {
background-color: #dadada;
}
.pkt_ext_containersaved .pkt_ext_btn_disabled,
.pkt_ext_containersaved .pkt_ext_btn_disabled:hover,
.pkt_ext_containersaved .pkt_ext_btn_disabled:active {
background-color: transparent;
cursor: default;
opacity: 0.4;
}
.pkt_ext_containersaved .pkt_ext_tag_error .pkt_ext_btn {
border: 1px solid #c3c3c3;
border-width: 1px 1px 1px 0;
height: 2.35em;
}
.pkt_ext_containersaved .autocomplete-suggestions {
margin-top: 2.2em;
}
/*=Recent/suggested tags
--------------------------------------------------------------------------------------- */
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detailshown {
border-top: 1px solid #c1c1c1;
bottom: 0;
box-sizing: border-box;
background: #ebebeb;
clear: both;
left: 0;
opacity: 0;
min-height: 110px;
position: fixed;
visibility: hidden;
width: 100%;
}
.pkt_ext_container_detailactive .pkt_ext_suggestedtag_detail,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detailshown {
opacity: 1;
visibility: visible;
}
.pkt_ext_containersaved .pkt_ext_suggestedtag_detailshown {
padding: 4px 0;
}
.pkt_ext_container_finalstate .pkt_ext_suggestedtag_detail {
opacity: 0;
visibility: hidden;
}
.pkt_ext_containersaved
.pkt_ext_containersaved .pkt_ext_recenttag_detail h4,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail h4 {
color: #333;
font-size: 0.8125em;
font-size: 13px;
font-weight: normal;
font-style: normal;
letter-spacing: normal;
margin: 0.5em 0;
text-align: left;
text-transform: none;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail .pkt_ext_loadingspinner,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail .pkt_ext_loadingspinner {
display: none;
position: absolute;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail_loading .pkt_ext_loadingspinner,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail_loading .pkt_ext_loadingspinner {
display: block;
font-size: 6px;
left: 48%;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail ul,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail ul {
display: block;
margin: 0;
height: 2em;
overflow: hidden;
padding: 2px 0 0 0;
}
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail ul {
height: auto;
margin: 0;
max-height: 4em;
padding-top: 6px;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail li,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail li {
background: none;
float: left;
height: inherit;
line-height: 1.5;
list-style: none;
margin-bottom: 0.5em;
width: inherit;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail li:before,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail li:before {
content: none;
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail .recenttag_msg,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail .suggestedtag_msg {
color: #333;
font-size: 0.8125em;
line-height: 1.2;
left: auto;
position: absolute;
text-align: left;
top: 2em;
}
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail .suggestedtag_msg {
margin-right: 1.3em;
}
.pkt_ext_containersaved .token_tag {
border-radius: 4px;
background: #f7f7f7;
border: 1px solid #c3c3c3;
color: #333;
font-size: 0.875em;
font-size: 14px;
font-weight: normal;
letter-spacing: normal;
margin-right: 0.5em;
padding: 0.125em 0.625em;
text-decoration: none;
text-transform: none;
}
.pkt_ext_containersaved .token_tag:hover {
background-color: #008acb;
border-color: #008acb;
color: #fff;
text-decoration: none;
}
.pkt_ext_containersaved .token_tag:before,
.pkt_ext_containersaved .token_tag:after {
content: none;
}
.pkt_ext_containersaved .token_tag:hover span {
background-image: url(../img/tag_closeactive@1x.png);
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token_tag:hover span {
background-image: url(../img/tag_closeactive@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag,
.pkt_ext_containersaved .pkt_ext_recenttag_detail_disabled .token_tag:hover,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail_disabled .token_tag,
.pkt_ext_containersaved .pkt_ext_suggestedtag_detail_disabled .token_tag:hover {
background-color: #f7f7f7;
cursor: default;
opacity: 0.5;
}
.pkt_ext_containersaved .token_tag_inactive {
display: none;
}
/*=Premium upsell
--------------------------------------------------------------------------------------- */
.pkt_ext_detail .pkt_ext_premupsell {
background-color: #50bbb6;
display: block;
padding: 1.5em 0;
text-align: center;
}
.pkt_ext_premupsell h4 {
color: #fff;
font-size: 1em;
margin-bottom: 1em;
}
.pkt_ext_premupsell a {
color: #28605d;
border-bottom: 1px solid #47a7a3;
font-weight: normal;
}
.pkt_ext_premupsell a:hover {
color: #14302f;
}
/*=Token input/autocomplete
--------------------------------------------------------------------------------------- */
.token-input-dropdown-tag {
border-radius: 4px;
box-sizing: border-box;
background: #fff;
border: 1px solid #cdcdcd;
margin-top: 0.5em;
left: 0 !important;
overflow-y: auto;
top: 1.9em !important;
z-index: 9000;
}
.token-input-dropdown-tag ul {
height: inherit;
max-height: 115px;
margin: 0;
overflow: auto;
padding: 0.5em 0;
}
.token-input-dropdown-tag ul li {
background: none;
color: #333;
font-weight: normal;
font-size: 1em;
float: none;
height: inherit;
letter-spacing: normal;
list-style: none;
padding: 0.75em;
text-align: left;
text-transform: none;
width: inherit;
}
.token-input-dropdown-tag ul li:before {
content: none;
}
.token-input-dropdown ul li.token-input-selected-dropdown-item {
background-color: #008acb;
color: #fff;
}
.token-input-list {
list-style: none;
margin: 0;
padding: 0;
}
.token-input-list li {
text-align: left;
list-style: none;
}
.token-input-list li input {
border: 0;
background-color: white;
}
.pkt_ext_containersaved .token-input-token {
background: none;
border-radius: 4px;
border: 1px solid #c3c3c3;
overflow: hidden;
margin: 0;
padding: 0 8px;
background-color: #f7f7f7;
color: #000;
font-weight: normal;
cursor: default;
line-height: 1.5;
display: block;
width: auto;
margin: 0 0.2em;
float: left;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled {
position: relative;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled input {
opacity: 0.5;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-list {
opacity: 0.5;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .pkt_ext_tag_input_blocker {
height: 100%;
left: 0;
position: absolute;
top: 0;
width: 100%;
z-index: 5;
}
.pkt_ext_containersaved .token-input-token p {
display: inline-block;
font-size: 14px;
font-weight: normal;
line-height: inherit;
letter-spacing: normal;
padding: 0;
margin: 0;
text-transform: none;
vertical-align: top;
width: auto;
}
.pkt_ext_containersaved .token-input-token p:before {
content: none;
width: 0;
}
.pkt_ext_containersaved .token-input-token span {
background: url(../img/tag_close@1x.png) center center no-repeat;
cursor: pointer;
display: inline-block;
height: 8px;
margin: 0 0 0 8px;
overflow: hidden;
width: 8px;
text-indent: -99px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token-input-token span {
background-image: url(../img/tag_close@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .token-input-selected-token {
background-color: #008acb;
border-color: #008acb;
color: #fff;
}
.pkt_ext_containersaved .token-input-selected-token span {
background-image: url(../img/tag_closeactive@1x.png);
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersaved .token-input-selected-token span {
background-image: url(../img/tag_closeactive@2x.png);
background-size: 8px 8px;
}
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token {
background-color: #f7f7f7;
}
.pkt_ext_containersaved .pkt_ext_tag_input_wrapper_disabled .token-input-selected-token span {
color: #bbb;
}
/*=Overflow mode
--------------------------------------------------------------------------------------- */
.pkt_ext_saved_overflow .pkt_ext_logo {
float: none;
margin: 0.5em auto 0;
}
.pkt_ext_saved_overflow .pkt_ext_initload {
top: -8px;
}
.pkt_ext_saved_overflow .pkt_ext_loadingspinner {
top: 10em;
}
.pkt_ext_saved_overflow .pkt_ext_topdetail {
float: none;
margin: 0 auto;
padding: 0 1em;
}
.pkt_ext_saved_overflow h2 {
margin-bottom: 0.5em;
margin-top: 0;
text-align: center;
}
.pkt_ext_saved_overflow .pkt_ext_item_actions ul {
display: inline-block;
width: auto;
}
.pkt_ext_saved_overflow .pkt_ext_item_actions li {
float: none;
padding-left: 1em;
padding-right: 1em;
text-align: center;
}
.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_removeitem,
.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_openpocket {
float: none;
text-align: center;
padding-left: 0;
padding-right: 0;
}
.pkt_ext_saved_overflow .pkt_ext_item_actions .pkt_ext_actions_separator {
display: none;
}
.pkt_ext_saved_overflow .pkt_ext_tag_detail {
margin-top: 0;
}
.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detail,
.pkt_ext_saved_overflow .pkt_ext_suggestedtag_detailshown {
top: 14.75em;
}
.pkt_ext_saved_overflow .pkt_ext_edit_msg {
top: 16em;
}
.pkt_ext_container_finalerrorstate.pkt_ext_saved_overflow .pkt_ext_errordetail {
box-sizing: border-box;
left: 0;
padding-left: 1em;
padding-right: 1em;
text-align: center;
top: 8.3em;
width: 100%;
}
/*=Language overrides
--------------------------------------------------------------------------------------- */
.pkt_ext_saved_es .pkt_ext_btn {
min-width: 5em;
}
.pkt_ext_saved_de .pkt_ext_btn,
.pkt_ext_saved_ru .pkt_ext_btn {
min-width: 6em;
}

View file

@ -1,424 +0,0 @@
/* signup.css
*
* Description:
* With base elements out of the way, this sets all custom styling for the extension.
*
* Contents:
* Global
* Core detail
* Core detail - storyboard
* Buttons
* Overflow mode
* Language overrides
*/
/*=Global
--------------------------------------------------------------------------------------- */
.pkt_ext_containersignup {
background-color: #ebebeb;
color: #333;
display: block;
font-size: 16px;
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
position: relative;
text-align: center;
}
.pkt_ext_containersignup_inactive {
animation: pkt_ext_hide 0.3s ease-out;
opacity: 0;
visibility: hidden;
}
.pkt_ext_cf:after {
content: " ";
display: table;
clear: both;
}
@keyframes pkt_ext_hide {
0% {
opacity: 1;
visibility: visible;
}
99% {
opacity: 0;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
/*=Core detail
--------------------------------------------------------------------------------------- */
.pkt_ext_containersignup p {
font-size: 14px;
color: #333;
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.3;
margin: 0 auto 1.5em;
max-width: 260px;
}
.pkt_ext_containersignup a {
color: #4c8fd0;
}
.pkt_ext_containersignup a:hover {
color: #3076b9;
}
.pkt_ext_containersignup .pkt_ext_introdetail {
background-color: #fbfbfb;
border: 1px solid #c1c1c1;
border-width: 0 0 1px;
}
.pkt_ext_containersignup .pkt_ext_logo {
background: url(../img/pocketlogo@1x.png) center bottom no-repeat;
display: block;
height: 32px;
margin: 0 auto 15px;
padding-top: 25px;
position: relative;
text-indent: -9999px;
width: 123px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .pkt_ext_logo {
background-image: url(../img/pocketlogo@2x.png);
background-size: 123px 32px;
}
}
.pkt_ext_containersignup .pkt_ext_introimg {
background: url(../img/pocketsignup_hero@1x.png) center center no-repeat;
display: block;
height: 125px;
margin: 0 auto;
position: relative;
text-indent: -9999px;
width: 255px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .pkt_ext_introimg {
background-image: url(../img/pocketsignup_hero@2x.png);
background-size: 255px 125px;
}
}
.pkt_ext_containersignup .pkt_ext_tagline {
margin-bottom: 0.5em;
}
.pkt_ext_containersignup .pkt_ext_learnmore {
font-size: 12px;
}
.pkt_ext_containersignup .pkt_ext_learnmoreinactive {
visibility: hidden;
}
.pkt_ext_signupdetail h4 {
font-size: 12px;
font-weight: normal;
}
.pkt_ext_signupdetail .btn-container {
position: relative;
margin-bottom: 0.8em;
}
.pkt_ext_containersignup .ff_signuphelp {
background: url(../img/signup_help@1x.png) center center no-repeat;
display: block;
height: 18px;
margin-top: -9px;
right: -15px;
position: absolute;
text-indent: -9999px;
width: 18px;
top: 50%;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .ff_signuphelp {
background-image: url(../img/signup_help@2x.png);
background-size: 18px 18px;
}
}
.pkt_ext_containersignup .alreadyhave {
font-size: 12px;
max-width: 320px;
margin-top: 15px;
}
.pkt_ext_containersignup .tryitnowspace {
margin-top: 22px;
}
.pkt_ext_signupdetail p.pkt_ext_tos {
color: #777;
font-size: 10px;
line-height: 1.5;
margin-top: 17px;
padding-top: 0;
max-width: 190px;
}
/*=Core detail - storyboard
--------------------------------------------------------------------------------------- */
.pkt_ext_introstory {
align-items: center;
display: flex;
padding: 20px;
}
.pkt_ext_introstory:after {
clear: both;
content: "";
display: table;
}
.pkt_ext_introstory p {
margin-bottom: 0;
text-align: left;
}
.pkt_ext_introstoryone {
padding: 20px 18px 15px 20px;
}
.pkt_ext_introstorytwo {
padding: 3px 0 0 20px;
}
.pkt_ext_introstorytwo .pkt_ext_tagline {
margin-bottom: 1.5em;
}
.pkt_ext_introstory_text {
flex: 1;
}
.pkt_ext_introstoryone_img,
.pkt_ext_introstorytwo_img {
display: block;
overflow: hidden;
position: relative;
text-indent: -999px;
}
.pkt_ext_introstoryone_img {
background: url(../img/pocketsignup_button@1x.png) center right no-repeat;
height: 82px;
padding: 0 0 0 0.7em;
width: 82px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_introstoryone_img {
background-image: url(../img/pocketsignup_button@2x.png);
background-size: 82px 82px;
}
}
.pkt_ext_introstorytwo_img {
background: url(../img/pocketsignup_devices@1x.png) bottom right no-repeat;
height: 110px;
padding: 1em 0 0 0.7em;
width: 124px;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_introstorytwo_img {
background-image: url(../img/pocketsignup_devices@2x.png);
background-size: 124px 110px;
}
}
.pkt_ext_introstorydivider {
border-top: 1px solid #c1c1c1;
height: 1px;
margin: 0 auto;
width: 125px;
}
/*=Buttons
--------------------------------------------------------------------------------------- */
.pkt_ext_containersignup .btn {
background-color: #0096dd;
border: 1px solid #0095dd;
border-radius: 2px;
color: #fff;
display: inline-block;
font-family: "FiraSans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: normal;
line-height: 1;
margin: 0;
padding: 11px 45px;
text-align: center;
text-decoration: none;
text-shadow: 0 -1px 0 rgba(142,4,17,0.5);
transition: background-color 0.1s linear;
width: auto;
}
.pkt_ext_containersignup .btn-secondary {
background-color: #fbfbfb;
border-color: #c1c1c1;
color: #444;
text-shadow: 0 1px 0 rgba(255,255,255,0.5);
}
.pkt_ext_containersignup .btn-small {
padding: 6px 20px;
}
.pkt_ext_containersignup .btn-mini {
font-size: 14px;
padding: 5px 15px 4px;
}
.pkt_ext_containersignup .btn:hover {
background-color: #008acb;
color: #fff;
text-decoration: none;
}
.pkt_ext_containersignup .btn-secondary:hover,
.pkt_ext_containersignup .btn-important:hover {
background-color: #f6f6f6;
color: #222;
}
.pkt_ext_containersignup .btn-disabled {
background-image: none;
color: #ccc;
color: rgba(255,255,255,0.6);
cursor: default;
opacity: 0.9;
}
.pkt_ext_containersignup .signup-btn-firefox,
.pkt_ext_containersignup .signup-btn-tryitnow,
.pkt_ext_containersignup .signup-btn-email,
.pkt_ext_containersignup .signupinterim-btn-login,
.pkt_ext_containersignup .signupinterim-btn-signup,
.pkt_ext_containersignup .forgot-btn-submit,
.pkt_ext_containersignup .forgotreset-btn-change {
min-width: 12.125em;
padding: 0.8em 1.1875em;
box-sizing: content-box;
}
.pkt_ext_containersignup .signup-btn-email {
position: relative;
z-index: 10;
}
.pkt_ext_containersignup .signup-btn-tryitnow,
.pkt_ext_containersignup .signup-btn-firefox {
min-width: 14.5em;
position: relative;
padding: 0;
}
.pkt_ext_containersignup .signup-btn-tryitnow{
margin-top: 25px;
}
.pkt_ext_containersignup .signup-btn-firefox .logo {
background: url(../img/signup_firefoxlogo@1x.png) center center no-repeat;
height: 2.6em;
left: 10px;
margin: 0;
padding: 0;
width: 22px;
position: absolute;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_containersignup .signup-btn-firefox .logo {
background-image: url(../img/signup_firefoxlogo@2x.png);
background-size: 22px 22px;
}
}
.pkt_ext_containersignup .forgotreset-btn-change {
margin-bottom: 2em;
}
.pkt_ext_containersignup .signup-btn-tryitnow .text,
.pkt_ext_containersignup .signup-btn-firefox .text {
display: inline-block;
padding: 0.8em 1.625em;
position: relative;
text-shadow: none;
white-space: nowrap;
}
.pkt_ext_containersignup .signup-btn-tryitnow .text,
.pkt_ext_containersignup .signup-btn-firefox .text {
color: #fff;
}
.pkt_ext_containersignup .btn-disabled .text {
color: #ccc;
color: rgba(255,255,255,0.6);
}
/*=Overflow mode
--------------------------------------------------------------------------------------- */
.pkt_ext_signup_overflow .pkt_ext_tagline {
margin-bottom: 1em;
padding: 0 1em;
}
.pkt_ext_signup_overflow .pkt_ext_introimg {
background-size: 200px 98px;
height: 98px;
width: 200px;
}
.pkt_ext_signup_overflow .signup-btn-firefox,
.pkt_ext_containersignup .signup-btn-tryitnow,
.pkt_ext_signup_overflow .signup-btn-email {
font-size: 14px;
min-width: 12.6em;
padding-left: 0.75em;
padding-right: 0.75em;
}
.pkt_ext_signup_overflow .signup-btn-tryitnow .text,
.pkt_ext_signup_overflow .signup-btn-firefox .text {
padding-left: 0;
padding-right: 0;
}
/*=Language overrides
--------------------------------------------------------------------------------------- */
.pkt_ext_signup_de .pkt_ext_introstoryone_img {
margin-right: -5px;
padding-left: 0;
}
.pkt_ext_signup_de .pkt_ext_introstorytwo .pkt_ext_tagline,
.pkt_ext_signup_es .pkt_ext_introstorytwo .pkt_ext_tagline,
.pkt_ext_signup_ja .pkt_ext_introstorytwo .pkt_ext_tagline,
.pkt_ext_signup_ru .pkt_ext_introstorytwo .pkt_ext_tagline {
margin-bottom: 0.5em;
}
.pkt_ext_signup_de .signup-btn-firefox .text,
.pkt_ext_signup_de .signup-btn-tryitnow .text,
.pkt_ext_signup_de .signup-btn-email,
.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-firefox .text,
.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-email,
.pkt_ext_signup_ja .signup-btn-firefox .text,
.pkt_ext_signup_ja .signup-btn-tryitnow .text,
.pkt_ext_signup_ja .signup-btn-email,
.pkt_ext_signup_ru .signup-btn-firefox .text,
.pkt_ext_signup_ru .signup-btn-tryitnow .text,
.pkt_ext_signup_ru .signup-btn-email {
font-size: 15px;
}
.pkt_ext_signup_ja .signup-btn-firefox .text,
.pkt_ext_signup_ja .signup-btn-tryitnow .text,
.pkt_ext_signup_ru .signup-btn-firefox .text,
.pkt_ext_signup_ru .signup-btn-tryitnow .text {
left: 15px;
}
.pkt_ext_signup_de .signup-btn-firefox .logo,
.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-firefox .logo,
.pkt_ext_signup_ja .signup-btn-firefox .logo,
.pkt_ext_signup_ru .signup-btn-firefox .logo {
height: 2.4em;
}
@media (min-resolution: 1.1dppx) {
.pkt_ext_signup_de .signup-btn-firefox .logo,
.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-firefox .logo,
.pkt_ext_signup_ja .signup-btn-firefox .logo,
.pkt_ext_signup_ru .signup-btn-firefox .logo {
height: 2.5em;
}
}
.pkt_ext_signup_de .signup-btn-email,
.pkt_ext_signup_es .pkt_ext_signupdetail_hero .signup-btn-email,
.pkt_ext_signup_ja .signup-btn-email,
.pkt_ext_signup_ru .signup-btn-email {
min-width: 13em;
padding: 0.8533em 1.2667em;
}
.pkt_ext_signup_de .pkt_ext_logo,
.pkt_ext_signup_es .pkt_ext_logo,
.pkt_ext_signup_ru .pkt_ext_logo {
padding-top: 15px;
}
.pkt_ext_signup_de .pkt_ext_introdetailhero .pkt_ext_tagline,
.pkt_ext_signup_es .pkt_ext_introdetailhero .pkt_ext_tagline,
.pkt_ext_signup_ja .pkt_ext_introdetailhero .pkt_ext_tagline,
.pkt_ext_signup_ru .pkt_ext_introdetailhero .pkt_ext_tagline {
font-size: 13px;
}
.pkt_ext_signup_overflow.pkt_ext_signup_de .signup-btn-firefox .logo,
.pkt_ext_signup_overflow.pkt_ext_signup_es .signup-btn-firefox .logo,
.pkt_ext_signup_overflow.pkt_ext_signup_ja .signup-btn-firefox .logo,
.pkt_ext_signup_overflow.pkt_ext_signup_ru .signup-btn-firefox .logo {
display: none;
}

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-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/. -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24">
<style>
use:not(:target) {
display: none;
}
use {
fill: #808080;
}
use[id$="-added"] {
fill: #ee4056;
}
</style>
<defs>
<path id="pocket-mark-shape" d="M21.901,4.204C21.642,3.484,20.956,3,20.196,3h-0.01h-1.721H3.814C3.067,3,2.385,3.474,2.119,4.179 C2.04,4.388,2,4.606,2,4.828v6.082l0.069,1.21c0.29,2.751,1.707,5.155,3.899,6.832c0.039,0.03,0.079,0.06,0.119,0.089l0.025,0.018 c1.175,0.866,2.491,1.452,3.91,1.741C10.677,20.932,11.347,21,12.013,21c0.615,0,1.232-0.057,1.839-0.171 c0.073-0.014,0.145-0.028,0.219-0.044c0.02-0.004,0.042-0.012,0.064-0.023c1.359-0.299,2.621-0.87,3.753-1.704l0.025-0.018 c0.04-0.029,0.08-0.059,0.119-0.089c2.192-1.677,3.609-4.08,3.898-6.832L22,10.91V4.828C22,4.618,21.975,4.409,21.901,4.204z M17.667,10.539l-4.704,4.547c-0.266,0.256-0.608,0.385-0.949,0.385c-0.342,0-0.684-0.129-0.949-0.385l-4.705-4.547 c-0.547-0.528-0.565-1.403-0.04-1.954c0.524-0.551,1.392-0.569,1.939-0.041l3.756,3.63l3.755-3.63 c0.547-0.528,1.415-0.51,1.939,0.04C18.231,9.136,18.213,10.011,17.667,10.539z"/>
</defs>
<use id="pocket-mark" xlink:href="#pocket-mark-shape"/>
<use id="pocket-mark-added" xlink:href="#pocket-mark-shape"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

View file

@ -1,78 +0,0 @@
// Documentation of methods used here are at:
// https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Interaction_between_privileged_and_non-privileged_pages
var pktPanelMessaging = (function() {
function panelIdFromURL(url) {
var panelId = url.match(/panelId=([\w|\d|\.]*)&?/);
if (panelId && panelId.length > 1) {
return panelId[1];
}
return 0;
}
function prefixedMessageId(messageId) {
return 'PKT_' + messageId;
}
function panelPrefixedMessageId(panelId, messageId) {
return prefixedMessageId(panelId + '_' + messageId);
}
function addMessageListener(panelId, messageId, callback) {
document.addEventListener(panelPrefixedMessageId(panelId, messageId), function(e) {
callback(JSON.parse(e.target.getAttribute("payload"))[0]);
// TODO: Figure out why e.target.parentNode is null
// e.target.parentNode.removeChild(e.target);
}, false);
}
function removeMessageListener(panelId, messageId, callback) {
document.removeEventListener(panelPrefixedMessageId(panelId, messageId), callback);
}
function sendMessage(panelId, messageId, payload, callback) {
// Payload needs to be an object in format:
// { panelId: panelId, data: {} }
var messagePayload = {
panelId: panelId,
data: (payload || {})
};
// Create a callback to listen for a response
if (callback) {
var messageResponseId = messageId + "Response";
var responseListener = function(responsePayload) {
callback(responsePayload);
removeMessageListener(panelId, messageResponseId, responseListener);
}
addMessageListener(panelId, messageResponseId, responseListener);
}
// Send message
var element = document.createElement("PKTMessageFromPanelElement");
element.setAttribute("payload", JSON.stringify([messagePayload]));
document.documentElement.appendChild(element);
var evt = document.createEvent("Events");
evt.initEvent(prefixedMessageId(messageId), true, false);
element.dispatchEvent(evt);
}
/**
* Public functions
*/
return {
panelIdFromURL: panelIdFromURL,
addMessageListener : addMessageListener,
removeMessageListener : removeMessageListener,
sendMessage: sendMessage
};
}());

View file

@ -1,608 +0,0 @@
/*
PKT_SAVED_OVERLAY is the view itself and contains all of the methods to manipute the overlay and messaging.
It does not contain any logic for saving or communication with the extension or server.
*/
var PKT_SAVED_OVERLAY = function (options)
{
var myself = this;
this.inited = false;
this.active = false;
this.wrapper = null;
this.pockethost = "getpocket.com";
this.savedItemId = 0;
this.savedUrl = '';
this.premiumStatus = false;
this.preventCloseTimerCancel = false;
this.closeValid = true;
this.mouseInside = false;
this.autocloseTimer = null;
this.inoverflowmenu = false;
this.dictJSON = {};
this.autocloseTiming = 3500;
this.autocloseTimingFinalState = 2000;
this.mouseInside = false;
this.userTags = [];
this.cxt_suggested_available = 0;
this.cxt_entered = 0;
this.cxt_suggested = 0;
this.cxt_removed = 0;
this.justaddedsuggested = false;
this.fillTagContainer = function(tags, container, tagclass) {
container.children().remove();
for (var i = 0; i < tags.length; i++) {
var newtag = $('<li><a href="#" class="token_tag"></a></li>');
newtag.find('a').text(tags[i]);
newtag.addClass(tagclass);
container.append(newtag);
this.cxt_suggested_available++;
}
};
this.fillUserTags = function() {
thePKT_SAVED.sendMessage("getTags", {}, function(resp)
{
if (typeof resp == 'object' && typeof resp.tags == 'object')
{
myself.userTags = resp.tags;
}
});
};
this.fillSuggestedTags = function()
{
if (!$('.pkt_ext_suggestedtag_detail').length)
{
myself.suggestedTagsLoaded = true;
myself.startCloseTimer();
return;
}
thePKT_SAVED.sendMessage("getSuggestedTags",
{
url: myself.savedUrl
}, function(resp)
{
$('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
if (resp.status == 'success')
{
var newtags = [];
for (var i = 0; i < resp.value.suggestedTags.length; i++)
{
newtags.push(resp.value.suggestedTags[i].tag);
}
myself.suggestedTagsLoaded = true;
if (!myself.mouseInside) {
myself.startCloseTimer();
}
myself.fillTagContainer(newtags, $('.pkt_ext_suggestedtag_detail ul'), 'token_suggestedtag');
}
else if (resp.status == 'error') {
var msg = $('<p class="suggestedtag_msg">');
msg.text(resp.error.message);
$('.pkt_ext_suggestedtag_detail').append(msg);
this.suggestedTagsLoaded = true;
if (!myself.mouseInside) {
myself.startCloseTimer();
}
}
});
}
this.initAutoCloseEvents = function() {
this.wrapper.on('mouseenter', function() {
myself.mouseInside = true;
myself.stopCloseTimer();
});
this.wrapper.on('mouseleave', function() {
myself.mouseInside = false;
myself.startCloseTimer();
});
this.wrapper.on('click', function(e) {
myself.closeValid = false;
});
};
this.startCloseTimer = function(manualtime)
{
var settime = manualtime ? manualtime : myself.autocloseTiming;
if (typeof myself.autocloseTimer == 'number')
{
clearTimeout(myself.autocloseTimer);
}
myself.autocloseTimer = setTimeout(function()
{
if (myself.closeValid || myself.preventCloseTimerCancel)
{
myself.preventCloseTimerCancel = false;
myself.closePopup();
}
}, settime);
};
this.stopCloseTimer = function()
{
if (myself.preventCloseTimerCancel)
{
return;
}
clearTimeout(myself.autocloseTimer);
};
this.closePopup = function() {
myself.stopCloseTimer();
thePKT_SAVED.sendMessage("close");
};
this.checkValidTagSubmit = function() {
var inputlength = $.trim($('.pkt_ext_tag_input_wrapper').find('.token-input-input-token').children('input').val()).length;
if ($('.pkt_ext_containersaved').find('.token-input-token').length || (inputlength > 0 && inputlength < 26))
{
$('.pkt_ext_containersaved').find('.pkt_ext_btn').removeClass('pkt_ext_btn_disabled');
}
else
{
$('.pkt_ext_containersaved').find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
}
myself.updateSlidingTagList();
};
this.updateSlidingTagList = function() {
var inputleft = $('.token-input-input-token input').position().left;
var listleft = $('.token-input-list').position().left;
var listleftmanual = parseInt($('.token-input-list').css('left'));
var listleftnatural = listleft - listleftmanual;
var leftwidth = $('.pkt_ext_tag_input_wrapper').outerWidth();
if ((inputleft + listleft + 20) > leftwidth)
{
$('.token-input-list').css('left', Math.min(((inputleft + listleftnatural - leftwidth + 20)*-1), 0) + 'px');
}
else
{
$('.token-input-list').css('left', '0');
}
};
this.checkPlaceholderStatus = function() {
if (this.wrapper.find('.pkt_ext_tag_input_wrapper').find('.token-input-token').length)
{
this.wrapper.find('.token-input-input-token input').attr('placeholder', '');
}
else
{
this.wrapper.find('.token-input-input-token input').attr('placeholder', $('.pkt_ext_tag_input').attr('placeholder')).css('width', '200px');
}
};
this.initTagInput = function() {
var inputwrapper = $('.pkt_ext_tag_input_wrapper');
inputwrapper.find('.pkt_ext_tag_input').tokenInput([], {
searchDelay: 200,
minChars: 1,
animateDropdown: false,
noResultsHideDropdown: true,
scrollKeyboard: true,
emptyInputLength: 200,
search_function: function(term, cb) {
var returnlist = [];
if (term.length) {
var limit = 15;
var r = new RegExp('^' + term);
for (var i = 0; i < myself.userTags.length; i++) {
if (r.test(myself.userTags[i]) && limit > 0) {
returnlist.push({name:myself.userTags[i]});
limit--;
}
}
}
if (!$('.token-input-dropdown-tag').data('init')) {
$('.token-input-dropdown-tag').css('width', inputwrapper.outerWidth()).data('init');
inputwrapper.append($('.token-input-dropdown-tag'));
}
cb(returnlist);
},
textToData: function(text) {
if ($.trim(text).length > 25 || !$.trim(text).length) {
if (text.length > 25) {
myself.showTagsError(myself.dictJSON.maxtaglength);
changestamp = Date.now();
setTimeout(function() {
$('.token-input-input-token input').val(text).focus();
}, 10);
}
return null;
}
myself.hideTagsError();
return {name:myself.sanitizeText(text.toLowerCase())};
},
onReady: function() {
$('.token-input-dropdown').addClass('token-input-dropdown-tag');
inputwrapper.find('.token-input-input-token input').attr('placeholder', $('.tag-input').attr('placeholder')).css('width', '200px');
if ($('.pkt_ext_suggestedtag_detail').length) {
myself.wrapper.find('.pkt_ext_suggestedtag_detail').on('click', '.token_tag', function(e) {
e.preventDefault();
var tag = $(e.target);
if ($(this).parents('.pkt_ext_suggestedtag_detail_disabled').length) {
return;
}
myself.justaddedsuggested = true;
inputwrapper.find('.pkt_ext_tag_input').tokenInput('add', {id:inputwrapper.find('.token-input-token').length, name:tag.text()});
tag.addClass('token-suggestedtag-inactive');
$('.token-input-input-token input').focus();
});
}
$('.token-input-list').on('keydown', 'input', function(e) {
if (e.which == 37) {
myself.updateSlidingTagList();
}
}).on('keypress', 'input', function(e) {
if (e.which == 13) {
if (typeof changestamp == 'undefined' || (Date.now() - changestamp > 250)) {
e.preventDefault();
myself.wrapper.find('.pkt_ext_btn').trigger('click');
}
}
}).on('keyup', 'input', function(e) {
myself.checkValidTagSubmit();
});
myself.checkPlaceholderStatus();
},
onAdd: function() {
myself.checkValidTagSubmit();
changestamp = Date.now();
myself.hideInactiveTags();
myself.checkPlaceholderStatus();
},
onDelete: function() {
myself.checkValidTagSubmit();
changestamp = Date.now();
myself.showActiveTags();
myself.checkPlaceholderStatus();
},
onShowDropdown: function() {
thePKT_SAVED.sendMessage("expandSavePanel");
},
onHideDropdown: function() {
thePKT_SAVED.sendMessage("collapseSavePanel");
}
});
$('body').on('keydown', function(e) {
var key = e.keyCode || e.which;
if (key == 8) {
var selected = $('.token-input-selected-token');
if (selected.length) {
e.preventDefault();
e.stopImmediatePropagation();
inputwrapper.find('.pkt_ext_tag_input').tokenInput('remove', {name:selected.find('p').text()});
}
}
else if ($(e.target).parent().hasClass('token-input-input-token')) {
e.stopImmediatePropagation();
}
});
};
this.disableInput = function() {
this.wrapper.find('.pkt_ext_item_actions').addClass('pkt_ext_item_actions_disabled');
this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
this.wrapper.find('.pkt_ext_tag_input_wrapper').addClass('pkt_ext_tag_input_wrapper_disabled');
if (this.wrapper.find('.pkt_ext_suggestedtag_detail').length) {
this.wrapper.find('.pkt_ext_suggestedtag_detail').addClass('pkt_ext_suggestedtag_detail_disabled');
}
};
this.enableInput = function() {
this.wrapper.find('.pkt_ext_item_actions').removeClass('pkt_ext_item_actions_disabled');
this.checkValidTagSubmit();
this.wrapper.find('.pkt_ext_tag_input_wrapper').removeClass('pkt_ext_tag_input_wrapper_disabled');
if (this.wrapper.find('.pkt_ext_suggestedtag_detail').length) {
this.wrapper.find('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_disabled');
}
};
this.initAddTagInput = function() {
$('.pkt_ext_btn').click(function(e) {
e.preventDefault();
if ($(this).hasClass('pkt_ext_btn_disabled') || $('.pkt_ext_edit_msg_active').filter('.pkt_ext_edit_msg_error').length)
{
return;
}
myself.disableInput();
$('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingtags);
var originaltags = [];
$('.token-input-token').each(function()
{
var text = $.trim($(this).find('p').text());
if (text.length)
{
originaltags.push(text);
}
});
thePKT_SAVED.sendMessage("addTags",
{
url: myself.savedUrl,
tags: originaltags
}, function(resp)
{
if (resp.status == 'success')
{
myself.showStateFinalMsg(myself.dictJSON.tagssaved);
}
else if (resp.status == 'error')
{
$('.pkt_ext_edit_msg').addClass('pkt_ext_edit_msg_error pkt_ext_edit_msg_active').text(resp.error.message);
}
});
});
};
this.initRemovePageInput = function() {
$('.pkt_ext_removeitem').click(function(e) {
if ($(this).parents('.pkt_ext_item_actions_disabled').length) {
e.preventDefault();
return;
}
if ($(this).hasClass('pkt_ext_removeitem')) {
e.preventDefault();
myself.disableInput();
$('.pkt_ext_containersaved').find('.pkt_ext_detail h2').text(myself.dictJSON.processingremove);
thePKT_SAVED.sendMessage("deleteItem",
{
itemId: myself.savedItemId
}, function(resp) {
if (resp.status == 'success') {
myself.showStateFinalMsg(myself.dictJSON.pageremoved);
}
else if (resp.status == 'error') {
$('.pkt_ext_edit_msg').addClass('pkt_ext_edit_msg_error pkt_ext_edit_msg_active').text(resp.error.message);
}
});
}
});
};
this.initOpenListInput = function() {
$('.pkt_ext_openpocket').click(function(e)
{
e.preventDefault();
thePKT_SAVED.sendMessage("openTabWithUrl",
{
url: $(this).attr('href'),
activate: true
});
myself.closePopup();
});
};
this.showTagsError = function(msg) {
$('.pkt_ext_edit_msg').addClass('pkt_ext_edit_msg_error pkt_ext_edit_msg_active').text(msg);
$('.pkt_ext_tag_detail').addClass('pkt_ext_tag_error');
};
this.hideTagsError = function(msg) {
$('.pkt_ext_edit_msg').removeClass('pkt_ext_edit_msg_error pkt_ext_edit_msg_active').text('');
$('.pkt_ext_tag_detail').removeClass('pkt_ext_tag_error');
};
this.showActiveTags = function() {
if (!$('.pkt_ext_suggestedtag_detail').length) {
return;
}
var activetokenstext = '|';
$('.token-input-token').each(function(index, element) {
activetokenstext += $(element).find('p').text() + '|';
});
var inactivetags = $('.pkt_ext_suggestedtag_detail').find('.token_tag_inactive');
inactivetags.each(function(index, element) {
if (activetokenstext.indexOf('|' + $(element).text() + '|') == -1) {
$(element).removeClass('token_tag_inactive');
}
});
};
this.hideInactiveTags = function() {
if (!$('.pkt_ext_suggestedtag_detail').length) {
return;
}
var activetokenstext = '|';
$('.token-input-token').each(function(index, element) {
activetokenstext += $(element).find('p').text() + '|';
});
var activesuggestedtags = $('.token_tag').not('.token_tag_inactive');
activesuggestedtags.each(function(index, element) {
if (activetokenstext.indexOf('|' + $(element).text() + '|') > -1) {
$(element).addClass('token_tag_inactive');
}
});
};
this.showStateSaved = function(initobj) {
this.wrapper.find('.pkt_ext_detail h2').text(this.dictJSON.pagesaved);
this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
if (typeof initobj.item == 'object')
{
this.savedItemId = initobj.item.item_id;
this.savedUrl = initobj.item.given_url;
}
$('.pkt_ext_containersaved').addClass('pkt_ext_container_detailactive').removeClass('pkt_ext_container_finalstate');
myself.fillUserTags();
if (myself.suggestedTagsLoaded) {
myself.startCloseTimer();
}
else {
myself.fillSuggestedTags();
}
};
this.sanitizeText = function(s) {
var sanitizeMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;'
};
if (typeof s !== 'string')
{
return '';
}
return String(s).replace(/[&<>"']/g, function (str) {
return sanitizeMap[str];
});
};
this.showStateFinalMsg = function(msg) {
this.wrapper.find('.pkt_ext_tag_detail').one('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd', function(e)
{
$(this).off('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd');
myself.preventCloseTimerCancel = true;
myself.startCloseTimer(myself.autocloseTimingFinalState);
myself.wrapper.find('.pkt_ext_detail h2').text(msg);
});
this.wrapper.addClass('pkt_ext_container_finalstate');
};
this.showStateError = function(headline, detail) {
this.wrapper.find('.pkt_ext_detail h2').text(headline);
this.wrapper.find('.pkt_ext_detail h3').text(detail);
this.wrapper.addClass('pkt_ext_container_detailactive pkt_ext_container_finalstate pkt_ext_container_finalerrorstate');
this.preventCloseTimerCancel = true;
this.startCloseTimer(myself.autocloseTimingFinalState);
}
this.getTranslations = function()
{
this.dictJSON = window.pocketStrings;
};
};
PKT_SAVED_OVERLAY.prototype = {
create : function()
{
if (this.active)
{
return;
}
this.active = true;
// set translations
this.getTranslations();
// set host
this.dictJSON.pockethost = this.pockethost;
// extra modifier class for collapsed state
if (this.inoverflowmenu)
{
$('body').addClass('pkt_ext_saved_overflow');
}
// extra modifier class for language
if (this.locale)
{
$('body').addClass('pkt_ext_saved_' + this.locale);
}
// Create actual content
$('body').append(Handlebars.templates.saved_shell(this.dictJSON));
// Add in premium content (if applicable based on premium status)
this.createPremiumFunctionality();
// Initialize functionality for overlay
this.wrapper = $('.pkt_ext_containersaved');
this.initTagInput();
this.initAddTagInput();
this.initRemovePageInput();
this.initOpenListInput();
this.initAutoCloseEvents();
},
createPremiumFunctionality: function()
{
if (this.premiumStatus && !$('.pkt_ext_suggestedtag_detail').length)
{
$('body').append(Handlebars.templates.saved_premiumshell(this.dictJSON));
$('.pkt_ext_initload').append(Handlebars.templates.saved_premiumextras(this.dictJSON));
}
}
};
// Layer between Bookmarklet and Extensions
var PKT_SAVED = function () {};
PKT_SAVED.prototype = {
init: function () {
if (this.inited) {
return;
}
this.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
this.overlay = new PKT_SAVED_OVERLAY();
this.inited = true;
},
addMessageListener: function(messageId, callback) {
pktPanelMessaging.addMessageListener(this.panelId, messageId, callback);
},
sendMessage: function(messageId, payload, callback) {
pktPanelMessaging.sendMessage(this.panelId, messageId, payload, callback);
},
create: function() {
var myself = this;
var url = window.location.href.match(/premiumStatus=([\w|\d|\.]*)&?/);
if (url && url.length > 1)
{
myself.overlay.premiumStatus = (url[1] == '1');
}
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
if (host && host.length > 1)
{
myself.overlay.pockethost = host[1];
}
var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
if (inoverflowmenu && inoverflowmenu.length > 1)
{
myself.overlay.inoverflowmenu = (inoverflowmenu[1] == 'true');
}
var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
if (locale && locale.length > 1)
{
myself.overlay.locale = locale[1].toLowerCase();
}
myself.overlay.create();
// tell back end we're ready
thePKT_SAVED.sendMessage("show");
// wait confirmation of save before flipping to final saved state
thePKT_SAVED.addMessageListener("saveLink", function(resp)
{
if (resp.status == 'error') {
if (typeof resp.error == 'object')
{
if (resp.error.localizedKey)
{
myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved, myself.overlay.dictJSON[resp.error.localizedKey]);
}
else
{
myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved, resp.error.message);
}
}
else
{
myself.overlay.showStateError(myself.overlay.dictJSON.pagenotsaved, myself.overlay.dictJSON.errorgeneric);
}
return;
}
myself.overlay.showStateSaved(resp);
});
}
}
$(function()
{
if (!window.thePKT_SAVED) {
var thePKT_SAVED = new PKT_SAVED();
window.thePKT_SAVED = thePKT_SAVED;
thePKT_SAVED.init();
}
var pocketHost = thePKT_SAVED.overlay.pockethost;
// send an async message to get string data
thePKT_SAVED.sendMessage("initL10N", {
tos: [
'https://'+ pocketHost +'/tos?s=ffi&t=tos&tv=panel_tryit',
'https://'+ pocketHost +'/privacy?s=ffi&t=privacypolicy&tv=panel_tryit'
]
}, function(resp) {
window.pocketStrings = resp.strings;
window.thePKT_SAVED.create();
});
});

View file

@ -1,193 +0,0 @@
/*
PKT_SIGNUP_OVERLAY is the view itself and contains all of the methods to manipute the overlay and messaging.
It does not contain any logic for saving or communication with the extension or server.
*/
var PKT_SIGNUP_OVERLAY = function (options)
{
var myself = this;
this.inited = false;
this.active = false;
this.delayedStateSaved = false;
this.wrapper = null;
this.variant = window.___PKT__SIGNUP_VARIANT;
this.tagline = window.___PKT__SIGNUP_TAGLINE || '';
this.preventCloseTimerCancel = false;
this.translations = {};
this.closeValid = true;
this.mouseInside = false;
this.autocloseTimer = null;
this.variant = "";
this.inoverflowmenu = false;
this.controlvariant;
this.pockethost = "getpocket.com";
this.fxasignedin = false;
this.dictJSON = {};
this.initCloseTabEvents = function() {
$('.btn,.pkt_ext_learnmore,.alreadyhave > a').click(function(e)
{
e.preventDefault();
thePKT_SIGNUP.sendMessage("openTabWithUrl",
{
url: $(this).attr('href'),
activate: true
});
myself.closePopup();
});
};
this.closePopup = function() {
thePKT_SIGNUP.sendMessage("close");
};
this.sanitizeText = function(s) {
var sanitizeMap = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;'
};
if (typeof s !== 'string')
{
return '';
}
return String(s).replace(/[&<>"']/g, function (str) {
return sanitizeMap[str];
});
};
this.getTranslations = function()
{
this.dictJSON = window.pocketStrings;
};
};
PKT_SIGNUP_OVERLAY.prototype = {
create : function()
{
var controlvariant = window.location.href.match(/controlvariant=([\w|\.]*)&?/);
if (controlvariant && controlvariant.length > 1)
{
this.controlvariant = controlvariant[1];
}
var variant = window.location.href.match(/variant=([\w|\.]*)&?/);
if (variant && variant.length > 1)
{
this.variant = variant[1];
}
var fxasignedin = window.location.href.match(/fxasignedin=([\w|\d|\.]*)&?/);
if (fxasignedin && fxasignedin.length > 1)
{
this.fxasignedin = (fxasignedin[1] == '1');
}
var host = window.location.href.match(/pockethost=([\w|\.]*)&?/);
if (host && host.length > 1)
{
this.pockethost = host[1];
}
var inoverflowmenu = window.location.href.match(/inoverflowmenu=([\w|\.]*)&?/);
if (inoverflowmenu && inoverflowmenu.length > 1)
{
this.inoverflowmenu = (inoverflowmenu[1] == 'true');
}
var locale = window.location.href.match(/locale=([\w|\.]*)&?/);
if (locale && locale.length > 1)
{
this.locale = locale[1].toLowerCase();
}
if (this.active)
{
return;
}
this.active = true;
// set translations
this.getTranslations();
this.dictJSON.fxasignedin = this.fxasignedin ? 1 : 0;
this.dictJSON.controlvariant = this.controlvariant == 'true' ? 1 : 0;
this.dictJSON.variant = (this.variant ? this.variant : 'undefined');
this.dictJSON.variant += this.fxasignedin ? '_fxa' : '_nonfxa';
this.dictJSON.pockethost = this.pockethost;
this.dictJSON.showlearnmore = true;
// extra modifier class for collapsed state
if (this.inoverflowmenu)
{
$('body').addClass('pkt_ext_signup_overflow');
}
// extra modifier class for language
if (this.locale)
{
$('body').addClass('pkt_ext_signup_' + this.locale);
}
// Create actual content
if (this.variant == 'overflow')
{
$('body').append(Handlebars.templates.signup_shell(this.dictJSON));
}
else
{
$('body').append(Handlebars.templates.signupstoryboard_shell(this.dictJSON));
}
// tell background we're ready
thePKT_SIGNUP.sendMessage("show");
// close events
this.initCloseTabEvents();
}
};
// Layer between Bookmarklet and Extensions
var PKT_SIGNUP = function () {};
PKT_SIGNUP.prototype = {
init: function () {
if (this.inited) {
return;
}
this.panelId = pktPanelMessaging.panelIdFromURL(window.location.href);
this.overlay = new PKT_SIGNUP_OVERLAY();
this.inited = true;
},
addMessageListener: function(messageId, callback) {
pktPanelMessaging.addMessageListener(this.panelId, messageId, callback);
},
sendMessage: function(messageId, payload, callback) {
pktPanelMessaging.sendMessage(this.panelId, messageId, payload, callback);
},
create: function() {
this.overlay.create();
// tell back end we're ready
thePKT_SIGNUP.sendMessage("show");
}
}
$(function()
{
if (!window.thePKT_SIGNUP) {
var thePKT_SIGNUP = new PKT_SIGNUP();
window.thePKT_SIGNUP = thePKT_SIGNUP;
thePKT_SIGNUP.init();
}
var pocketHost = thePKT_SIGNUP.overlay.pockethost;
// send an async message to get string data
thePKT_SIGNUP.sendMessage("initL10N", {
tos: [
'https://'+ pocketHost +'/tos?s=ffi&t=tos&tv=panel_tryit',
'https://'+ pocketHost +'/privacy?s=ffi&t=privacypolicy&tv=panel_tryit'
]
}, function(resp) {
window.pocketStrings = resp.strings;
window.thePKT_SIGNUP.create();
});
});

View file

@ -1,242 +0,0 @@
(function() {
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates['saved_premiumextras'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
return "<div class=\"pkt_ext_suggestedtag_detailshown\">\r\n</div> ";
},"useData":true});
templates['saved_premiumshell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return "<div class=\"pkt_ext_suggestedtag_detail pkt_ext_suggestedtag_detail_loading\">\n <h4>"
+ escapeExpression(((helper = (helper = helpers.suggestedtags || (depth0 != null ? depth0.suggestedtags : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"suggestedtags","hash":{},"data":data}) : helper)))
+ "</h4>\n <div class=\"pkt_ext_loadingspinner\"><div></div></div>\n <ul class=\"pkt_ext_cf\">\n </ul>\n</div>";
},"useData":true});
templates['saved_shell'] = template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return "<div class=\"pkt_ext_initload\">\n <div class=\"pkt_ext_logo\"></div> \n <div class=\"pkt_ext_topdetail\">\n <h2>"
+ escapeExpression(((helper = (helper = helpers.saving || (depth0 != null ? depth0.saving : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"saving","hash":{},"data":data}) : helper)))
+ "</h2>\n </div> \n <div class=\"pkt_ext_loadingspinner\"><div></div></div>\n</div> \n<div class=\"pkt_ext_detail\"> \n <div class=\"pkt_ext_logo\"></div>\n <div class=\"pkt_ext_topdetail\">\n <h2>"
+ escapeExpression(((helper = (helper = helpers.pagesaved || (depth0 != null ? depth0.pagesaved : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pagesaved","hash":{},"data":data}) : helper)))
+ "</h2>\n <h3 class=\"pkt_ext_errordetail\"></h3>\n <nav class=\"pkt_ext_item_actions pkt_ext_cf\">\n <ul>\n <li><a class=\"pkt_ext_removeitem\" href=\"#\">"
+ escapeExpression(((helper = (helper = helpers.removepage || (depth0 != null ? depth0.removepage : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"removepage","hash":{},"data":data}) : helper)))
+ "</a></li>\n <li class=\"pkt_ext_actions_separator\"></li> \n <li><a class=\"pkt_ext_openpocket\" href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/a?src=ff_ext_saved\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.viewlist || (depth0 != null ? depth0.viewlist : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"viewlist","hash":{},"data":data}) : helper)))
+ "</a></li>\n </ul>\n </nav> \n </div>\n <div class=\"pkt_ext_tag_detail pkt_ext_cf\">\n <div class=\"pkt_ext_tag_input_wrapper\">\n <div class=\"pkt_ext_tag_input_blocker\"></div>\n <input class=\"pkt_ext_tag_input\" type=\"text\" placeholder=\""
+ escapeExpression(((helper = (helper = helpers.addtags || (depth0 != null ? depth0.addtags : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"addtags","hash":{},"data":data}) : helper)))
+ "\">\n </div>\n <a href=\"#\" class=\"pkt_ext_btn pkt_ext_btn_disabled\">"
+ escapeExpression(((helper = (helper = helpers.save || (depth0 != null ? depth0.save : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"save","hash":{},"data":data}) : helper)))
+ "</a>\n </div>\n <p class=\"pkt_ext_edit_msg\"></p>\n</div>";
},"useData":true});
templates['signup_shell'] = template({"1":function(depth0,helpers,partials,data) {
var stack1, buffer = "";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.controlvariant : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(4, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer;
},"2":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"pkt_ext_learnmorecontainer\"><a class=\"pkt_ext_learnmore\" href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_learnmore?s=ffi&t=learnmore&tv=panel_control&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"4":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"pkt_ext_learnmorecontainer\"><a class=\"pkt_ext_learnmore\" href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_learnmore?s=ffi&t=learnmore&tv=panel_tryit&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"6":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p class=\"pkt_ext_learnmorecontainer\"><a class=\"pkt_ext_learnmore pkt_ext_learnmoreinactive\" href=\"#\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"8":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s=ffi&t=signupff&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&src=extension&s=ffi&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n";
},"10":function(depth0,helpers,partials,data) {
var stack1, buffer = "";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.controlvariant : depth0), {"name":"if","hash":{},"fn":this.program(11, data),"inverse":this.program(13, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer;
},"11":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s=ffi&tv=panel_control&t=signupff&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/signup?force=email&tv=panel_control&src=extension&s=ffi&t=signupemail&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
+ "</a></p>\n <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&tv=panel_control&src=extension&s=ffi&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n";
},"13":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_tryitnow?s=ffi&tv=panel_tryit&t=tryitnow\" target=\"_blank\" class=\"btn signup-btn-tryitnow\"><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.tryitnow || (depth0 != null ? depth0.tryitnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tryitnow","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"alreadyhave tryitnowspace\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&s=ffi&tv=panel_tryit&src=extension&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n <p class=\"pkt_ext_tos\">";
stack1 = ((helper = (helper = helpers.tos || (depth0 != null ? depth0.tos : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tos","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
return buffer + "</p>\n";
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail pkt_ext_introdetailhero\">\n <h2 class=\"pkt_ext_logo\">Pocket</h2>\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.tagline || (depth0 != null ? depth0.tagline : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tagline","hash":{},"data":data}) : helper)))
+ "</p>\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.showlearnmore : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(6, data),"data":data});
if (stack1 != null) { buffer += stack1; }
buffer += " <div class=\"pkt_ext_introimg\"></div>\n</div>\n<div class=\"pkt_ext_signupdetail pkt_ext_signupdetail_hero\">\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(8, data),"inverse":this.program(10, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer + "</div>\n";
},"useData":true});
templates['signupstoryboard_shell'] = template({"1":function(depth0,helpers,partials,data) {
var stack1, buffer = "";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.controlvariant : depth0), {"name":"if","hash":{},"fn":this.program(2, data),"inverse":this.program(4, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer;
},"2":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p><a class=\"pkt_ext_learnmore\" href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_learnmore?s=ffi&t=learnmore&tv=panel_control&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"4":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p><a class=\"pkt_ext_learnmore\" href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_learnmore?s=ffi&t=learnmore&tv=panel_tryit&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"6":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <p><a class=\"pkt_ext_learnmore pkt_ext_learnmoreinactive\" href=\"#\">"
+ escapeExpression(((helper = (helper = helpers.learnmore || (depth0 != null ? depth0.learnmore : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"learnmore","hash":{},"data":data}) : helper)))
+ "</a></p>\n";
},"8":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s=ffi&t=signupff&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signinfirefox || (depth0 != null ? depth0.signinfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signinfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&src=extension&s=ffi&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n";
},"10":function(depth0,helpers,partials,data) {
var stack1, buffer = "";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.controlvariant : depth0), {"name":"if","hash":{},"fn":this.program(11, data),"inverse":this.program(13, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer;
},"11":function(depth0,helpers,partials,data) {
var helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression;
return " <h4>"
+ escapeExpression(((helper = (helper = helpers.signuptosave || (depth0 != null ? depth0.signuptosave : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signuptosave","hash":{},"data":data}) : helper)))
+ "</h4>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/ff_signup?s=ffi&tv=panel_control&t=signupff&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn signup-btn-firefox\"><span class=\"logo\"></span><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.signupfirefox || (depth0 != null ? depth0.signupfirefox : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupfirefox","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/signup?force=email&tv=panel_control&src=extension&s=ffi&t=signupemail&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\" class=\"btn btn-secondary signup-btn-email signup-btn-initstate\">"
+ escapeExpression(((helper = (helper = helpers.signupemail || (depth0 != null ? depth0.signupemail : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"signupemail","hash":{},"data":data}) : helper)))
+ "</a></p>\n <p class=\"alreadyhave\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&tv=panel_control&src=extension&s=ffi&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n";
},"13":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = " <p class=\"btn-container\"><a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/firefox_tryitnow?s=ffi&tv=panel_tryit&t=tryitnow\" target=\"_blank\" class=\"btn signup-btn-tryitnow\"><span class=\"text\">"
+ escapeExpression(((helper = (helper = helpers.tryitnow || (depth0 != null ? depth0.tryitnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tryitnow","hash":{},"data":data}) : helper)))
+ "</span></a></p>\n <p class=\"alreadyhave tryitnowspace\">"
+ escapeExpression(((helper = (helper = helpers.alreadyhaveacct || (depth0 != null ? depth0.alreadyhaveacct : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"alreadyhaveacct","hash":{},"data":data}) : helper)))
+ " <a href=\"https://"
+ escapeExpression(((helper = (helper = helpers.pockethost || (depth0 != null ? depth0.pockethost : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"pockethost","hash":{},"data":data}) : helper)))
+ "/login?ep=3&s=ffi&tv=panel_tryit&src=extension&t=login&v="
+ escapeExpression(((helper = (helper = helpers.variant || (depth0 != null ? depth0.variant : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"variant","hash":{},"data":data}) : helper)))
+ "\" target=\"_blank\">"
+ escapeExpression(((helper = (helper = helpers.loginnow || (depth0 != null ? depth0.loginnow : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"loginnow","hash":{},"data":data}) : helper)))
+ "</a>.</p>\n <p class=\"pkt_ext_tos\">";
stack1 = ((helper = (helper = helpers.tos || (depth0 != null ? depth0.tos : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"tos","hash":{},"data":data}) : helper));
if (stack1 != null) { buffer += stack1; }
return buffer + "</p>\n";
},"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
var stack1, helper, functionType="function", helperMissing=helpers.helperMissing, escapeExpression=this.escapeExpression, buffer = "<div class=\"pkt_ext_introdetail pkt_ext_introdetailstoryboard\">\n <div class=\"pkt_ext_introstory pkt_ext_introstoryone\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.taglinestory_one || (depth0 != null ? depth0.taglinestory_one : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_one","hash":{},"data":data}) : helper)))
+ "</p>\n </div>\n <div class=\"pkt_ext_introstoryone_img\"></div>\n </div>\n <div class=\"pkt_ext_introstorydivider\"></div>\n <div class=\"pkt_ext_introstory pkt_ext_introstorytwo\">\n <div class=\"pkt_ext_introstory_text\">\n <p class=\"pkt_ext_tagline\">"
+ escapeExpression(((helper = (helper = helpers.taglinestory_two || (depth0 != null ? depth0.taglinestory_two : depth0)) != null ? helper : helperMissing),(typeof helper === functionType ? helper.call(depth0, {"name":"taglinestory_two","hash":{},"data":data}) : helper)))
+ "</p>\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.showlearnmore : depth0), {"name":"if","hash":{},"fn":this.program(1, data),"inverse":this.program(6, data),"data":data});
if (stack1 != null) { buffer += stack1; }
buffer += " </div>\n <div class=\"pkt_ext_introstorytwo_img\"></div>\n </div>\n</div>\n<div class=\"pkt_ext_signupdetail\">\n";
stack1 = helpers['if'].call(depth0, (depth0 != null ? depth0.fxasignedin : depth0), {"name":"if","hash":{},"fn":this.program(8, data),"inverse":this.program(10, data),"data":data});
if (stack1 != null) { buffer += stack1; }
return buffer + "\n</div>\n";
},"useData":true});
})();

View file

@ -1,660 +0,0 @@
/*
handlebars v2.0.0
Copyright (C) 2011-2014 by Yehuda Katz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@license
*/
/* exported Handlebars */
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.Handlebars = root.Handlebars || factory();
}
}(this, function () {
// handlebars/safe-string.js
var __module3__ = (function() {
"use strict";
var __exports__;
// Build out our basic SafeString type
function SafeString(string) {
this.string = string;
}
SafeString.prototype.toString = function() {
return "" + this.string;
};
__exports__ = SafeString;
return __exports__;
})();
// handlebars/utils.js
var __module2__ = (function(__dependency1__) {
"use strict";
var __exports__ = {};
/*jshint -W004 */
var SafeString = __dependency1__;
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
function escapeChar(chr) {
return escape[chr];
}
function extend(obj /* , ...source */) {
for (var i = 1; i < arguments.length; i++) {
for (var key in arguments[i]) {
if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
obj[key] = arguments[i][key];
}
}
}
return obj;
}
__exports__.extend = extend;var toString = Object.prototype.toString;
__exports__.toString = toString;
// Sourced from lodash
// https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
var isFunction = function(value) {
return typeof value === 'function';
};
// fallback for older versions of Chrome and Safari
/* istanbul ignore next */
if (isFunction(/x/)) {
isFunction = function(value) {
return typeof value === 'function' && toString.call(value) === '[object Function]';
};
}
var isFunction;
__exports__.isFunction = isFunction;
/* istanbul ignore next */
var isArray = Array.isArray || function(value) {
return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
};
__exports__.isArray = isArray;
function escapeExpression(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof SafeString) {
return string.toString();
} else if (string == null) {
return "";
} else if (!string) {
return string + '';
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
string = "" + string;
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
}
__exports__.escapeExpression = escapeExpression;function isEmpty(value) {
if (!value && value !== 0) {
return true;
} else if (isArray(value) && value.length === 0) {
return true;
} else {
return false;
}
}
__exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) {
return (contextPath ? contextPath + '.' : '') + id;
}
__exports__.appendContextPath = appendContextPath;
return __exports__;
})(__module3__);
// handlebars/exception.js
var __module4__ = (function() {
"use strict";
var __exports__;
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
function Exception(message, node) {
var line;
if (node && node.firstLine) {
line = node.firstLine;
message += ' - ' + line + ':' + node.firstColumn;
}
var tmp = Error.prototype.constructor.call(this, message);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
if (line) {
this.lineNumber = line;
this.column = node.firstColumn;
}
}
Exception.prototype = new Error();
__exports__ = Exception;
return __exports__;
})();
// handlebars/base.js
var __module1__ = (function(__dependency1__, __dependency2__) {
"use strict";
var __exports__ = {};
var Utils = __dependency1__;
var Exception = __dependency2__;
var VERSION = "2.0.0";
__exports__.VERSION = VERSION;var COMPILER_REVISION = 6;
__exports__.COMPILER_REVISION = COMPILER_REVISION;
var REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '== 1.0.0-rc.3',
3: '== 1.0.0-rc.4',
4: '== 1.x.x',
5: '== 2.0.0-alpha.x',
6: '>= 2.0.0-beta.1'
};
__exports__.REVISION_CHANGES = REVISION_CHANGES;
var isArray = Utils.isArray,
isFunction = Utils.isFunction,
toString = Utils.toString,
objectType = '[object Object]';
function HandlebarsEnvironment(helpers, partials) {
this.helpers = helpers || {};
this.partials = partials || {};
registerDefaultHelpers(this);
}
__exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
constructor: HandlebarsEnvironment,
logger: logger,
log: log,
registerHelper: function(name, fn) {
if (toString.call(name) === objectType) {
if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
Utils.extend(this.helpers, name);
} else {
this.helpers[name] = fn;
}
},
unregisterHelper: function(name) {
delete this.helpers[name];
},
registerPartial: function(name, partial) {
if (toString.call(name) === objectType) {
Utils.extend(this.partials, name);
} else {
this.partials[name] = partial;
}
},
unregisterPartial: function(name) {
delete this.partials[name];
}
};
function registerDefaultHelpers(instance) {
instance.registerHelper('helperMissing', function(/* [args, ]options */) {
if(arguments.length === 1) {
// A missing field in a {{foo}} constuct.
return undefined;
} else {
// Someone is actually trying to call something, blow up.
throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
}
});
instance.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse,
fn = options.fn;
if(context === true) {
return fn(this);
} else if(context === false || context == null) {
return inverse(this);
} else if (isArray(context)) {
if(context.length > 0) {
if (options.ids) {
options.ids = [options.name];
}
return instance.helpers.each(context, options);
} else {
return inverse(this);
}
} else {
if (options.data && options.ids) {
var data = createFrame(options.data);
data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
options = {data: data};
}
return fn(context, options);
}
});
instance.registerHelper('each', function(context, options) {
if (!options) {
throw new Exception('Must pass iterator to #each');
}
var fn = options.fn, inverse = options.inverse;
var i = 0, ret = "", data;
var contextPath;
if (options.data && options.ids) {
contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
}
if (isFunction(context)) { context = context.call(this); }
if (options.data) {
data = createFrame(options.data);
}
if(context && typeof context === 'object') {
if (isArray(context)) {
for(var j = context.length; i<j; i++) {
if (data) {
data.index = i;
data.first = (i === 0);
data.last = (i === (context.length-1));
if (contextPath) {
data.contextPath = contextPath + i;
}
}
ret = ret + fn(context[i], { data: data });
}
} else {
for(var key in context) {
if(context.hasOwnProperty(key)) {
if(data) {
data.key = key;
data.index = i;
data.first = (i === 0);
if (contextPath) {
data.contextPath = contextPath + key;
}
}
ret = ret + fn(context[key], {data: data});
i++;
}
}
}
}
if(i === 0){
ret = inverse(this);
}
return ret;
});
instance.registerHelper('if', function(conditional, options) {
if (isFunction(conditional)) { conditional = conditional.call(this); }
// Default behavior is to render the positive path if the value is truthy and not empty.
// The `includeZero` option may be set to treat the condtional as purely not empty based on the
// behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative.
if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
instance.registerHelper('unless', function(conditional, options) {
return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash});
});
instance.registerHelper('with', function(context, options) {
if (isFunction(context)) { context = context.call(this); }
var fn = options.fn;
if (!Utils.isEmpty(context)) {
if (options.data && options.ids) {
var data = createFrame(options.data);
data.contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]);
options = {data:data};
}
return fn(context, options);
} else {
return options.inverse(this);
}
});
instance.registerHelper('log', function(message, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
instance.log(level, message);
});
instance.registerHelper('lookup', function(obj, field) {
return obj && obj[field];
});
}
var logger = {
methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' },
// State enum
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
level: 3,
// can be overridden in the host environment
log: function(level, message) {
if (logger.level <= level) {
var method = logger.methodMap[level];
if (typeof console !== 'undefined' && console[method]) {
console[method].call(console, message);
}
}
}
};
__exports__.logger = logger;
var log = logger.log;
__exports__.log = log;
var createFrame = function(object) {
var frame = Utils.extend({}, object);
frame._parent = object;
return frame;
};
__exports__.createFrame = createFrame;
return __exports__;
})(__module2__, __module4__);
// handlebars/runtime.js
var __module5__ = (function(__dependency1__, __dependency2__, __dependency3__) {
"use strict";
var __exports__ = {};
var Utils = __dependency1__;
var Exception = __dependency2__;
var COMPILER_REVISION = __dependency3__.COMPILER_REVISION;
var REVISION_CHANGES = __dependency3__.REVISION_CHANGES;
var createFrame = __dependency3__.createFrame;
function checkRevision(compilerInfo) {
var compilerRevision = compilerInfo && compilerInfo[0] || 1,
currentRevision = COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
var runtimeVersions = REVISION_CHANGES[currentRevision],
compilerVersions = REVISION_CHANGES[compilerRevision];
throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").");
}
}
}
__exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial
function template(templateSpec, env) {
/* istanbul ignore next */
if (!env) {
throw new Exception("No environment passed to template");
}
if (!templateSpec || !templateSpec.main) {
throw new Exception('Unknown template object: ' + typeof templateSpec);
}
// Note: Using env.VM references rather than local var references throughout this section to allow
// for external users to override these as psuedo-supported APIs.
env.VM.checkRevision(templateSpec.compiler);
var invokePartialWrapper = function(partial, indent, name, context, hash, helpers, partials, data, depths) {
if (hash) {
context = Utils.extend({}, context, hash);
}
var result = env.VM.invokePartial.call(this, partial, name, context, helpers, partials, data, depths);
if (result == null && env.compile) {
var options = { helpers: helpers, partials: partials, data: data, depths: depths };
partials[name] = env.compile(partial, { data: data !== undefined, compat: templateSpec.compat }, env);
result = partials[name](context, options);
}
if (result != null) {
if (indent) {
var lines = result.split('\n');
for (var i = 0, l = lines.length; i < l; i++) {
if (!lines[i] && i + 1 === l) {
break;
}
lines[i] = indent + lines[i];
}
result = lines.join('\n');
}
return result;
} else {
throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
}
};
// Just add water
var container = {
lookup: function(depths, name) {
var len = depths.length;
for (var i = 0; i < len; i++) {
if (depths[i] && depths[i][name] != null) {
return depths[i][name];
}
}
},
lambda: function(current, context) {
return typeof current === 'function' ? current.call(context) : current;
},
escapeExpression: Utils.escapeExpression,
invokePartial: invokePartialWrapper,
fn: function(i) {
return templateSpec[i];
},
programs: [],
program: function(i, data, depths) {
var programWrapper = this.programs[i],
fn = this.fn(i);
if (data || depths) {
programWrapper = program(this, i, fn, data, depths);
} else if (!programWrapper) {
programWrapper = this.programs[i] = program(this, i, fn);
}
return programWrapper;
},
data: function(data, depth) {
while (data && depth--) {
data = data._parent;
}
return data;
},
merge: function(param, common) {
var ret = param || common;
if (param && common && (param !== common)) {
ret = Utils.extend({}, common, param);
}
return ret;
},
noop: env.VM.noop,
compilerInfo: templateSpec.compiler
};
var ret = function(context, options) {
options = options || {};
var data = options.data;
ret._setup(options);
if (!options.partial && templateSpec.useData) {
data = initData(context, data);
}
var depths;
if (templateSpec.useDepths) {
depths = options.depths ? [context].concat(options.depths) : [context];
}
return templateSpec.main.call(container, context, container.helpers, container.partials, data, depths);
};
ret.isTop = true;
ret._setup = function(options) {
if (!options.partial) {
container.helpers = container.merge(options.helpers, env.helpers);
if (templateSpec.usePartial) {
container.partials = container.merge(options.partials, env.partials);
}
} else {
container.helpers = options.helpers;
container.partials = options.partials;
}
};
ret._child = function(i, data, depths) {
if (templateSpec.useDepths && !depths) {
throw new Exception('must pass parent depths');
}
return program(container, i, templateSpec[i], data, depths);
};
return ret;
}
__exports__.template = template;function program(container, i, fn, data, depths) {
var prog = function(context, options) {
options = options || {};
return fn.call(container, context, container.helpers, container.partials, options.data || data, depths && [context].concat(depths));
};
prog.program = i;
prog.depth = depths ? depths.length : 0;
return prog;
}
__exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data, depths) {
var options = { partial: true, helpers: helpers, partials: partials, data: data, depths: depths };
if(partial === undefined) {
throw new Exception("The partial " + name + " could not be found");
} else if(partial instanceof Function) {
return partial(context, options);
}
}
__exports__.invokePartial = invokePartial;function noop() { return ""; }
__exports__.noop = noop;function initData(context, data) {
if (!data || !('root' in data)) {
data = data ? createFrame(data) : {};
data.root = context;
}
return data;
}
return __exports__;
})(__module2__, __module4__, __module1__);
// handlebars.runtime.js
var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
"use strict";
var __exports__;
/*globals Handlebars: true */
var base = __dependency1__;
// Each of these augment the Handlebars object. No need to setup here.
// (This is done to easily share code between commonjs and browse envs)
var SafeString = __dependency2__;
var Exception = __dependency3__;
var Utils = __dependency4__;
var runtime = __dependency5__;
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
var create = function() {
var hb = new base.HandlebarsEnvironment();
Utils.extend(hb, base);
hb.SafeString = SafeString;
hb.Exception = Exception;
hb.Utils = Utils;
hb.escapeExpression = Utils.escapeExpression;
hb.VM = runtime;
hb.template = function(spec) {
return runtime.template(spec, hb);
};
return hb;
};
var Handlebars = create();
Handlebars.create = create;
Handlebars['default'] = Handlebars;
__exports__ = Handlebars;
return __exports__;
})(__module1__, __module3__, __module4__, __module2__, __module5__);
return __module0__;
}));

File diff suppressed because one or more lines are too long

View file

@ -1,954 +0,0 @@
/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
* Version 1.6.0
*
* Copyright (c) 2009 James Smith (http://loopj.com)
* Licensed jointly under the GPL and MIT licenses,
* choose which one suits your project best!
*
* Licensed under MIT
* With modifications
*
*/
(function ($) {
// Default settings
var DEFAULT_SETTINGS = {
// Search settings
method: "GET",
contentType: "json",
queryParam: "q",
searchDelay: 300,
minChars: 1,
propertyToSearch: "name",
jsonContainer: null,
scrollKeyboard: false,
// Display settings
hintText: null,
noResultsText: null,
noResultsHideDropdown: false,
searchingText: null,
deleteText: "&times;",
animateDropdown: true,
emptyInputLength: null,
// Tokenization settings
tokenLimit: null,
tokenDelimiter: ",",
preventDuplicates: false,
// Output settings
tokenValue: "id",
// Prepopulation settings
prePopulate: null,
processPrePopulate: false,
// Manipulation settings
idPrefix: "token-input-",
// Formatters
resultsFormatter: function(item) {
let listItem = document.createElement("li");
listItem.textContent = item[this.propertyToSearch];
return listItem.outerHTML;
},
tokenFormatter: function(item) {
let listItem = document.createElement("li");
let p = document.createElement("p");
p.textContent = item[this.propertyToSearch];
listItem.appendChild(p);
return listItem.outerHTML;
},
// Validations
validateItem: null,
// Force selections only on mouse click
noHoverSelect: false,
// Callbacks
onResult: null,
onAdd: null,
onDelete: null,
onReady: null
};
// Default classes to use when theming
var DEFAULT_CLASSES = {
tokenList: "token-input-list",
token: "token-input-token",
tokenDelete: "token-input-delete-token",
selectedToken: "token-input-selected-token",
highlightedToken: "token-input-highlighted-token",
dropdown: "token-input-dropdown",
dropdownItem: "token-input-dropdown-item",
dropdownItem2: "token-input-dropdown-item2",
selectedDropdownItem: "token-input-selected-dropdown-item",
inputToken: "token-input-input-token"
};
// Input box position "enum"
var POSITION = {
BEFORE: 0,
AFTER: 1,
END: 2
};
// Keys "enum"
var KEY = {
BACKSPACE: 8,
TAB: 9,
ENTER: 13,
ESCAPE: 27,
SPACE: 32,
PAGE_UP: 33,
PAGE_DOWN: 34,
END: 35,
HOME: 36,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
NUMPAD_ENTER: 108,
COMMA: 188
};
// Additional public (exposed) methods
var methods = {
init: function(url_or_data_or_function, options) {
var settings = $.extend({}, DEFAULT_SETTINGS, options || {});
return this.each(function () {
$(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings));
});
},
clear: function() {
this.data("tokenInputObject").clear();
return this;
},
add: function(item) {
this.data("tokenInputObject").add(item);
return this;
},
remove: function(item) {
this.data("tokenInputObject").remove(item);
return this;
},
get: function() {
return this.data("tokenInputObject").getTokens();
}
}
// Expose the .tokenInput function to jQuery as a plugin
$.fn.tokenInput = function (method) {
// Method calling and initialization logic
if(methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else {
return methods.init.apply(this, arguments);
}
};
// TokenList class for each input
$.TokenList = function (input, url_or_data, settings) {
//
// Initialization
//
// Configure the data source
if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") {
// Set the url to query against
settings.url = url_or_data;
// If the URL is a function, evaluate it here to do our initalization work
var url = computeURL();
// Make a smart guess about cross-domain if it wasn't explicitly specified
if(settings.crossDomain === undefined) {
if(url.indexOf("://") === -1) {
settings.crossDomain = false;
} else {
settings.crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]);
}
}
} else if(typeof(url_or_data) === "object") {
// Set the local data to search through
settings.local_data = url_or_data;
}
// Build class names
if(settings.classes) {
// Use custom class names
settings.classes = $.extend({}, DEFAULT_CLASSES, settings.classes);
} else if(settings.theme) {
// Use theme-suffixed default class names
settings.classes = {};
$.each(DEFAULT_CLASSES, function(key, value) {
settings.classes[key] = value + "-" + settings.theme;
});
} else {
settings.classes = DEFAULT_CLASSES;
}
// Save the tokens
var saved_tokens = [];
// Keep track of the number of tokens in the list
var token_count = 0;
// Basic cache to save on db hits
var cache = new $.TokenList.Cache();
// Keep track of the timeout, old vals
var timeout;
var input_val;
function tokenize(){
var item = $(selected_dropdown_item).data("tokeninput");
if(!item && settings.textToData){
item = settings.textToData(input_box.val());
}
if(item) {
add_token(item);
hidden_input.change();
return false;
}
}
// Create a new text input an attach keyup events
var input_box = $("<input type=\"text\" autocomplete=\"off\">")
.css({
outline: "none"
})
.attr("id", settings.idPrefix + input.id)
.focus(function () {
if (settings.minChars == 0) {
setTimeout(function(){do_search();}, 5);
}
if (settings.tokenLimit === null || settings.tokenLimit !== token_count) {
show_dropdown_hint();
}
})
.blur(function () {
tokenize();
hide_dropdown();
$(this).val("");
})
.bind("keyup keydown blur update", resize_input)
.keydown(function (event) {
var previous_token;
var next_token;
switch(event.keyCode) {
case KEY.LEFT:
case KEY.RIGHT:
case KEY.UP:
case KEY.DOWN:
if(!$(this).val()) {
previous_token = input_token.prev();
next_token = input_token.next();
if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) {
// Check if there is a previous/next token and it is selected
if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) {
deselect_token($(selected_token), POSITION.BEFORE);
} else {
deselect_token($(selected_token), POSITION.AFTER);
}
} else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) {
// We are moving left, select the previous token if it exists
select_token($(previous_token.get(0)));
} else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) {
// We are moving right, select the next token if it exists
select_token($(next_token.get(0)));
}
} else {
if (event.keyCode === KEY.UP || event.keyCode === KEY.DOWN) {
var dropdown_item = null;
if(!selected_dropdown_item && (event.keyCode === KEY.DOWN)) {
dropdown_item = $('.token-input-dropdown li').first();
}
else if(event.keyCode === KEY.DOWN) {
dropdown_item = $(selected_dropdown_item).next();
} else {
dropdown_item = $(selected_dropdown_item).prev();
}
if(dropdown_item.length) {
select_dropdown_item(dropdown_item,true);
}
else if (!(event.keyCode === KEY.DOWN) && $(selected_dropdown_item).length) {
deselect_dropdown_item($(selected_dropdown_item));
}
return false;
}
}
break;
case KEY.BACKSPACE:
previous_token = input_token.prev();
if(!$(this).val().length) {
if(selected_token) {
delete_token($(selected_token));
hidden_input.change();
} else if(previous_token.length) {
select_token($(previous_token.get(0)));
}
return false;
} else if($(this).val().length === 1) {
hide_dropdown();
} else {
// set a timeout just long enough to let this function finish.
setTimeout(function(){do_search();}, 5);
}
break;
case KEY.TAB:
case KEY.ENTER:
case KEY.NUMPAD_ENTER:
case KEY.COMMA:
if (event.keyCode != KEY.ENTER && event.keyCode != KEY.NUMPAD_ENTER)
{
event.preventDefault();
}
tokenize();
break;
case KEY.ESCAPE:
hide_dropdown();
return true;
default:
if(String.fromCharCode(event.which)) {
// set a timeout just long enough to let this function finish.
setTimeout(function(){do_search();}, 5);
}
break;
}
});
// Keep a reference to the original input box
var hidden_input = $(input)
.hide()
.val("")
.focus(function () {
input_box.focus();
})
.blur(function () {
input_box.blur();
});
// Keep a reference to the selected token and dropdown item
var selected_token = null;
var selected_token_index = 0;
var selected_dropdown_item = null;
// The list to store the token items in
var token_list = $("<ul />")
.addClass(settings.classes.tokenList)
.click(function (event) {
var li = $(event.target).closest("li");
if(li && li.get(0) && $.data(li.get(0), "tokeninput")) {
toggle_select_token(li);
} else {
// Deselect selected token
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
// Focus input box
input_box.focus();
}
})
.mouseover(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.addClass(settings.classes.highlightedToken);
}
})
.mouseout(function (event) {
var li = $(event.target).closest("li");
if(li && selected_token !== this) {
li.removeClass(settings.classes.highlightedToken);
}
})
.insertBefore(hidden_input);
// The token holding the input box
var input_token = $("<li />")
.addClass(settings.classes.inputToken)
.appendTo(token_list)
.append(input_box);
// The list to store the dropdown items in
var dropdown = $("<div>")
.addClass(settings.classes.dropdown)
.appendTo("body")
.hide();
// Magic element to help us resize the text input
var input_resizer = $("<tester/>")
.insertAfter(input_box)
.css({
position: "absolute",
top: -9999,
left: -9999,
width: "auto",
fontSize: input_box.css("fontSize"),
fontFamily: input_box.css("fontFamily"),
fontWeight: input_box.css("fontWeight"),
letterSpacing: input_box.css("letterSpacing"),
whiteSpace: "nowrap"
});
// Pre-populate list if items exist
hidden_input.val("");
var li_data = settings.prePopulate || hidden_input.data("pre");
if(settings.processPrePopulate && $.isFunction(settings.onResult)) {
li_data = settings.onResult.call(hidden_input, li_data);
}
if(li_data && li_data.length) {
$.each(li_data, function (index, value) {
insert_token(value);
checkTokenLimit();
});
}
// Initialization is done
if($.isFunction(settings.onReady)) {
settings.onReady.call();
if (settings.minChars == 0)
{
setTimeout(function(){do_search();}, 5);
}
}
//
// Public functions
//
this.clear = function() {
token_list.children("li").each(function() {
if ($(this).children("input").length === 0) {
delete_token($(this));
}
});
}
this.add = function(item) {
add_token(item);
}
this.remove = function(item) {
token_list.children("li").each(function() {
if ($(this).children("input").length === 0) {
var currToken = $(this).data("tokeninput");
var match = true;
for (var prop in item) {
if (item[prop] !== currToken[prop]) {
match = false;
break;
}
}
if (match) {
delete_token($(this));
}
}
});
}
this.getTokens = function() {
return saved_tokens;
}
//
// Private functions
//
function checkTokenLimit() {
if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
input_box.hide();
hide_dropdown();
return;
}
}
function resize_input() {
if(input_val === (input_val = input_box.val())) {return;}
// Enter new content into resizer and resize input accordingly
var escaped = input_val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
input_resizer.html(escaped);
var minwidth = 30;
if (settings.emptyInputLength && token_list.children().length < 2) {
minwidth = settings.emptyInputLength;
}
input_box.width(input_resizer.width() + minwidth);
}
function is_printable_character(keycode) {
return ((keycode >= 48 && keycode <= 90) || // 0-1a-z
(keycode >= 96 && keycode <= 111) || // numpad 0-9 + - / * .
(keycode >= 186 && keycode <= 192) || // ; = , - . / ^
(keycode >= 219 && keycode <= 222)); // ( \ ) '
}
// Inner function to a token to the list
function insert_token(item) {
var this_token = settings.tokenFormatter(item);
this_token = $(this_token)
.addClass(settings.classes.token)
.insertBefore(input_token);
// The 'delete token' button
$("<span>" + settings.deleteText + "</span>")
.addClass(settings.classes.tokenDelete)
.appendTo(this_token)
.click(function () {
delete_token($(this).parent());
hidden_input.change();
return false;
});
// Store data on the token
var token_data = {"id": item.id};
token_data[settings.propertyToSearch] = item[settings.propertyToSearch];
token_data.item = item;
$.data(this_token.get(0), "tokeninput", item);
// Save this token for duplicate checking
saved_tokens = saved_tokens.slice(0,selected_token_index).concat([token_data]).concat(saved_tokens.slice(selected_token_index));
selected_token_index++;
// Update the hidden input
update_hidden_input(saved_tokens, hidden_input);
token_count += 1;
// Check the token limit
if(settings.tokenLimit !== null && token_count >= settings.tokenLimit) {
input_box.hide();
hide_dropdown();
}
return this_token;
}
// Add a token to the token list based on user input
function add_token (item) {
if(!item) return;
// Check for item validation
if ($.isFunction(settings.validateItem) && !settings.validateItem(item)) {
return false;
}
var callback = settings.onAdd;
// See if the token already exists and select it if we don't want duplicates
if(token_count > 0 && settings.preventDuplicates) {
var found_existing_token = null;
token_list.children().each(function () {
var existing_token = $(this);
var existing_data = $.data(existing_token.get(0), "tokeninput");
if(existing_data && existing_data.id === item.id) {
found_existing_token = existing_token;
return false;
}
});
if(found_existing_token) {
select_token(found_existing_token);
input_token.insertAfter(found_existing_token);
input_box.focus();
return;
}
}
// Insert the new tokens
if(settings.tokenLimit == null || token_count < settings.tokenLimit) {
insert_token(item);
checkTokenLimit();
}
// Clear input box
input_box.val("");
// Don't show the help dropdown, they've got the idea
hide_dropdown();
// Execute the onAdd callback if defined
if($.isFunction(callback)) {
callback.call(hidden_input,item);
}
}
// Select a token in the token list
function select_token (token) {
token.addClass(settings.classes.selectedToken);
selected_token = token.get(0);
// Hide input box
input_box.val("");
// Hide dropdown if it is visible (eg if we clicked to select token)
hide_dropdown();
}
// Deselect a token in the token list
function deselect_token (token, position) {
token.removeClass(settings.classes.selectedToken);
selected_token = null;
if(position === POSITION.BEFORE) {
input_token.insertBefore(token);
selected_token_index--;
} else if(position === POSITION.AFTER) {
input_token.insertAfter(token);
selected_token_index++;
} else {
input_token.appendTo(token_list);
selected_token_index = token_count;
}
// Show the input box and give it focus again
input_box.focus();
}
// Toggle selection of a token in the token list
function toggle_select_token(token) {
var previous_selected_token = selected_token;
if(selected_token) {
deselect_token($(selected_token), POSITION.END);
}
if(previous_selected_token === token.get(0)) {
deselect_token(token, POSITION.END);
} else {
select_token(token);
}
}
// Delete a token from the token list
function delete_token (token) {
// Remove the id from the saved list
var token_data = $.data(token.get(0), "tokeninput");
var callback = settings.onDelete;
var index = token.prevAll().length;
if(index > selected_token_index) index--;
// Delete the token
token.remove();
selected_token = null;
// Show the input box and give it focus again
input_box.focus();
// Remove this token from the saved list
saved_tokens = saved_tokens.slice(0,index).concat(saved_tokens.slice(index+1));
if(index < selected_token_index) selected_token_index--;
// Update the hidden input
update_hidden_input(saved_tokens, hidden_input);
token_count -= 1;
if(settings.tokenLimit !== null) {
input_box
.show()
.val("")
.focus();
}
// Execute the onDelete callback if defined
if($.isFunction(callback)) {
callback.call(hidden_input,token_data);
}
}
// Update the hidden input box value
function update_hidden_input(saved_tokens, hidden_input) {
var token_values = $.map(saved_tokens, function (el) {
return el[settings.tokenValue];
});
hidden_input.val(token_values.join(settings.tokenDelimiter));
}
// Hide and clear the results dropdown
function hide_dropdown () {
dropdown.hide().empty();
selected_dropdown_item = null;
if (settings.onHideDropdown)
settings.onHideDropdown();
}
function show_dropdown() {
dropdown
.css({
position: "absolute",
top: $(token_list).offset().top + $(token_list).outerHeight(),
left: $(token_list).offset().left,
zindex: 999
})
.show();
if (settings.onShowDropdown)
settings.onShowDropdown();
}
function show_dropdown_searching () {
if(settings.searchingText) {
dropdown.html("<p>"+settings.searchingText+"</p>");
show_dropdown();
}
}
function show_dropdown_hint () {
if(settings.hintText) {
dropdown.html("<p>"+settings.hintText+"</p>");
show_dropdown();
}
}
// Highlight the query part of the search term
function highlight_term(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<b>$1</b>");
}
function find_value_and_highlight_term(template, value, term) {
return template.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + value + ")(?![^<>]*>)(?![^&;]+;)", "g"), highlight_term(value, term));
}
// Populate the results dropdown with some results
function populate_dropdown (query, results) {
if(results && results.length) {
dropdown.empty();
var dropdown_ul = $("<ul>")
.appendTo(dropdown)
.mouseover(function (event) {
select_dropdown_item($(event.target).closest("li"));
})
.mousedown(function (event) {
add_token($(event.target).closest("li").data("tokeninput"));
hidden_input.change();
return false;
})
.hide();
if (settings.noHoverSelect) {
dropdown_ul.off('mouseover');
dropdown_ul.on('mouseover',function (event) {
$(this).find("li").removeClass(settings.classes.selectedDropdownItem);
$(event.target).closest("li").addClass(settings.classes.selectedDropdownItem);
});
}
$.each(results, function(index, value) {
var this_li = settings.resultsFormatter(value);
// this_li = find_value_and_highlight_term(this_li ,value[settings.propertyToSearch], query);
this_li = $(this_li).appendTo(dropdown_ul);
if(index % 2) {
this_li.addClass(settings.classes.dropdownItem);
} else {
this_li.addClass(settings.classes.dropdownItem2);
}
// if(index === 0) {
// select_dropdown_item(this_li);
// }
$.data(this_li.get(0), "tokeninput", value);
});
show_dropdown();
if(settings.animateDropdown) {
dropdown_ul.slideDown("fast");
} else {
dropdown_ul.show();
}
} else {
if(settings.noResultsText) {
dropdown.html("<p>"+settings.noResultsText+"</p>");
show_dropdown();
}
if (settings.noResultsHideDropdown) {
hide_dropdown();
}
}
}
// Highlight an item in the results dropdown
function select_dropdown_item (item,withkeyboard) {
if(item) {
if(selected_dropdown_item) {
deselect_dropdown_item($(selected_dropdown_item));
}
if (settings.scrollKeyboard && withkeyboard) {
var list = $('.token-input-dropdown-tag ul');
var listheight = list.height();
var itemheight = item.outerHeight();
var itemtop = item.position().top;
if (itemtop > listheight) {
var listscroll = list.scrollTop();
list.scrollTop(listscroll + itemheight);
}
else if (itemtop < 0) {
var listscroll = list.scrollTop();
list.scrollTop(listscroll - itemheight);
}
}
item.addClass(settings.classes.selectedDropdownItem);
selected_dropdown_item = item.get(0);
}
}
// Remove highlighting from an item in the results dropdown
function deselect_dropdown_item (item) {
item.removeClass(settings.classes.selectedDropdownItem);
selected_dropdown_item = null;
}
// Do a search and show the "searching" dropdown if the input is longer
// than settings.minChars
function do_search() {
var query = input_box.val().toLowerCase();
if(query && query.length || settings.minChars == 0) {
if(selected_token) {
deselect_token($(selected_token), POSITION.AFTER);
}
if(query.length >= settings.minChars) {
show_dropdown_searching();
clearTimeout(timeout);
timeout = setTimeout(function(){
run_search(query);
}, settings.searchDelay);
} else {
hide_dropdown();
}
}
}
// Do the actual search
function run_search(query) {
var cache_key = query + computeURL();
var cached_results = cache.get(cache_key);
if(cached_results) {
populate_dropdown(query, cached_results);
} else {
// Are we doing an ajax search or local data search?
if(settings.url) {
var url = computeURL();
// Extract exisiting get params
var ajax_params = {};
ajax_params.data = {};
if(url.indexOf("?") > -1) {
var parts = url.split("?");
ajax_params.url = parts[0];
var param_array = parts[1].split("&");
$.each(param_array, function (index, value) {
var kv = value.split("=");
ajax_params.data[kv[0]] = kv[1];
});
} else {
ajax_params.url = url;
}
// Prepare the request
ajax_params.data[settings.queryParam] = query;
ajax_params.type = settings.method;
ajax_params.dataType = settings.contentType;
if(settings.crossDomain) {
ajax_params.dataType = "jsonp";
}
// Attach the success callback
ajax_params.success = function(results) {
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(cache_key, settings.jsonContainer ? results[settings.jsonContainer] : results);
// only populate the dropdown if the results are associated with the active search query
if(input_box.val().toLowerCase() === query) {
populate_dropdown(query, settings.jsonContainer ? results[settings.jsonContainer] : results);
}
};
// Make the request
$.ajax(ajax_params);
} else if(settings.search_function){
settings.search_function(query, function(results){
cache.add(cache_key, results);
populate_dropdown(query, results);
});
} else if(settings.local_data) {
// Do the search through local data
var results = $.grep(settings.local_data, function (row) {
return row[settings.propertyToSearch].toLowerCase().indexOf(query.toLowerCase()) > -1;
});
if($.isFunction(settings.onResult)) {
results = settings.onResult.call(hidden_input, results);
}
cache.add(cache_key, results);
populate_dropdown(query, results);
}
}
}
// compute the dynamic URL
function computeURL() {
var url = settings.url;
if(typeof settings.url == 'function') {
url = settings.url.call();
}
return url;
}
};
// Really basic cache for the results
$.TokenList.Cache = function (options) {
var settings = $.extend({
max_size: 500
}, options);
var data = {};
var size = 0;
var flush = function () {
data = {};
size = 0;
};
this.add = function (query, results) {
if(size > settings.max_size) {
flush();
}
if(!data[query]) {
size += 1;
}
data[query] = results;
};
this.get = function (query) {
return data[query];
};
};
}(jQuery));

View file

@ -1,35 +0,0 @@
Unless where otherwise noted, the following license applies to the files
within this directory and descendents of this directory.
POCKET MARKS
Notwithstanding the permitted uses of the Software (as defined below) pursuant
to the license set forth below, "Pocket," "Read It Later" and the Pocket icon
and logos (collectively, the “Pocket Marks”) are registered and common law
trademarks of Read It Later, Inc. This means that, while you have considerable
freedom to redistribute and modify the Software, there are tight restrictions
on your ability to use the Pocket Marks. This license does not grant you any
rights to use the Pocket Marks except as they are embodied in the Software.
---
SOFTWARE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -1,19 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base href="chrome://pocket/content/panels/">
<title>Pocket: Page Saved</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/firasans.css">
<link rel="stylesheet" href="css/saved.css">
</head>
<body class="pkt_ext_containersaved" aria-live="polite">
<script type="text/javascript" src="js/vendor/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="js/vendor/handlebars.runtime.js"></script>
<script type="text/javascript" src="js/vendor/jquery.tokeninput.min.js"></script>
<script type="text/javascript" src="js/tmpl.js"></script>
<script type="text/javascript" src="js/messages.js"></script>
<script type="text/javascript" src="js/saved.js"></script>
</body>
</html>

View file

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<base href="chrome://pocket/content/panels/">
<title>Pocket: Sign Up</title>
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/firasans.css">
<link rel="stylesheet" href="css/signup.css">
</head>
<body class="pkt_ext_containersignup" aria-live="polite">
<script type="text/javascript" src="js/vendor/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="js/vendor/handlebars.runtime.js"></script>
<script type="text/javascript" src="js/tmpl.js"></script>
<script type="text/javascript" src="js/messages.js"></script>
<script type="text/javascript" src="js/signup.js"></script>
</body>
</html>

View file

@ -1,2 +0,0 @@
<div class="pkt_ext_suggestedtag_detailshown">
</div>

View file

@ -1,6 +0,0 @@
<div class="pkt_ext_suggestedtag_detail pkt_ext_suggestedtag_detail_loading">
<h4>{{suggestedtags}}</h4>
<div class="pkt_ext_loadingspinner"><div></div></div>
<ul class="pkt_ext_cf">
</ul>
</div>

View file

@ -1,29 +0,0 @@
<div class="pkt_ext_initload">
<div class="pkt_ext_logo"></div>
<div class="pkt_ext_topdetail">
<h2>{{saving}}</h2>
</div>
<div class="pkt_ext_loadingspinner"><div></div></div>
</div>
<div class="pkt_ext_detail">
<div class="pkt_ext_logo"></div>
<div class="pkt_ext_topdetail">
<h2>{{pagesaved}}</h2>
<h3 class="pkt_ext_errordetail"></h3>
<nav class="pkt_ext_item_actions pkt_ext_cf">
<ul>
<li><a class="pkt_ext_removeitem" href="#">{{removepage}}</a></li>
<li class="pkt_ext_actions_separator"></li>
<li><a class="pkt_ext_openpocket" href="https://{{pockethost}}/a?src=ff_ext_saved" target="_blank">{{viewlist}}</a></li>
</ul>
</nav>
</div>
<div class="pkt_ext_tag_detail pkt_ext_cf">
<div class="pkt_ext_tag_input_wrapper">
<div class="pkt_ext_tag_input_blocker"></div>
<input class="pkt_ext_tag_input" type="text" placeholder="{{addtags}}">
</div>
<a href="#" class="pkt_ext_btn pkt_ext_btn_disabled">{{save}}</a>
</div>
<p class="pkt_ext_edit_msg"></p>
</div>

View file

@ -1,32 +0,0 @@
<div class="pkt_ext_introdetail pkt_ext_introdetailhero">
<h2 class="pkt_ext_logo">Pocket</h2>
<p class="pkt_ext_tagline">{{tagline}}</p>
{{#if showlearnmore}}
{{#if controlvariant}}
<p class="pkt_ext_learnmorecontainer"><a class="pkt_ext_learnmore" href="https://{{pockethost}}/firefox_learnmore?s=ffi&t=learnmore&tv=panel_control&v={{variant}}" target="_blank">{{learnmore}}</a></p>
{{else}}
<p class="pkt_ext_learnmorecontainer"><a class="pkt_ext_learnmore" href="https://{{pockethost}}/firefox_learnmore?s=ffi&t=learnmore&tv=panel_tryit&v={{variant}}" target="_blank">{{learnmore}}</a></p>
{{/if}}
{{else}}
<p class="pkt_ext_learnmorecontainer"><a class="pkt_ext_learnmore pkt_ext_learnmoreinactive" href="#">{{learnmore}}</a></p>
{{/if}}
<div class="pkt_ext_introimg"></div>
</div>
<div class="pkt_ext_signupdetail pkt_ext_signupdetail_hero">
{{#if fxasignedin}}
<h4>{{signuptosave}}</h4>
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s=ffi&t=signupff&v={{variant}}" target="_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
<p class="alreadyhave">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&src=extension&s=ffi&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
{{else}}
{{#if controlvariant}}
<h4>{{signuptosave}}</h4>
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s=ffi&tv=panel_control&t=signupff&v={{variant}}" target="_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
<p class="btn-container"><a href="https://{{pockethost}}/signup?force=email&tv=panel_control&src=extension&s=ffi&t=signupemail&v={{variant}}" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
<p class="alreadyhave">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&tv=panel_control&src=extension&s=ffi&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
{{else}}
<p class="btn-container"><a href="https://{{pockethost}}/firefox_tryitnow?s=ffi&tv=panel_tryit&t=tryitnow" target="_blank" class="btn signup-btn-tryitnow"><span class="text">{{tryitnow}}</span></a></p>
<p class="alreadyhave tryitnowspace">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&s=ffi&tv=panel_tryit&src=extension&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
<p class="pkt_ext_tos">{{{tos}}}</p>
{{/if}}
{{/if}}
</div>

View file

@ -1,43 +0,0 @@
<div class="pkt_ext_introdetail pkt_ext_introdetailstoryboard">
<div class="pkt_ext_introstory pkt_ext_introstoryone">
<div class="pkt_ext_introstory_text">
<p class="pkt_ext_tagline">{{taglinestory_one}}</p>
</div>
<div class="pkt_ext_introstoryone_img"></div>
</div>
<div class="pkt_ext_introstorydivider"></div>
<div class="pkt_ext_introstory pkt_ext_introstorytwo">
<div class="pkt_ext_introstory_text">
<p class="pkt_ext_tagline">{{taglinestory_two}}</p>
{{#if showlearnmore}}
{{#if controlvariant}}
<p><a class="pkt_ext_learnmore" href="https://{{pockethost}}/firefox_learnmore?s=ffi&t=learnmore&tv=panel_control&v={{variant}}" target="_blank">{{learnmore}}</a></p>
{{else}}
<p><a class="pkt_ext_learnmore" href="https://{{pockethost}}/firefox_learnmore?s=ffi&t=learnmore&tv=panel_tryit&v={{variant}}" target="_blank">{{learnmore}}</a></p>
{{/if}}
{{else}}
<p><a class="pkt_ext_learnmore pkt_ext_learnmoreinactive" href="#">{{learnmore}}</a></p>
{{/if}}
</div>
<div class="pkt_ext_introstorytwo_img"></div>
</div>
</div>
<div class="pkt_ext_signupdetail">
{{#if fxasignedin}}
<h4>{{signuptosave}}</h4>
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s=ffi&t=signupff&v={{variant}}" target="_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signinfirefox}}</span></a></p>
<p class="alreadyhave">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&src=extension&s=ffi&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
{{else}}
{{#if controlvariant}}
<h4>{{signuptosave}}</h4>
<p class="btn-container"><a href="https://{{pockethost}}/ff_signup?s=ffi&tv=panel_control&t=signupff&v={{variant}}" target="_blank" class="btn signup-btn-firefox"><span class="logo"></span><span class="text">{{signupfirefox}}</span></a></p>
<p class="btn-container"><a href="https://{{pockethost}}/signup?force=email&tv=panel_control&src=extension&s=ffi&t=signupemail&v={{variant}}" target="_blank" class="btn btn-secondary signup-btn-email signup-btn-initstate">{{signupemail}}</a></p>
<p class="alreadyhave">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&tv=panel_control&src=extension&s=ffi&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
{{else}}
<p class="btn-container"><a href="https://{{pockethost}}/firefox_tryitnow?s=ffi&tv=panel_tryit&t=tryitnow" target="_blank" class="btn signup-btn-tryitnow"><span class="text">{{tryitnow}}</span></a></p>
<p class="alreadyhave tryitnowspace">{{alreadyhaveacct}} <a href="https://{{pockethost}}/login?ep=3&s=ffi&tv=panel_tryit&src=extension&t=login&v={{variant}}" target="_blank">{{loginnow}}</a>.</p>
<p class="pkt_ext_tos">{{{tos}}}</p>
{{/if}}
{{/if}}
</div>

View file

@ -1,657 +0,0 @@
/*
* LICENSE
*
* POCKET MARKS
*
* Notwithstanding the permitted uses of the Software (as defined below) pursuant to the license set forth below, "Pocket," "Read It Later" and the Pocket icon and logos (collectively, the Pocket Marks) are registered and common law trademarks of Read It Later, Inc. This means that, while you have considerable freedom to redistribute and modify the Software, there are tight restrictions on your ability to use the Pocket Marks. This license does not grant you any rights to use the Pocket Marks except as they are embodied in the Software.
*
* ---
*
* SOFTWARE
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
* Pocket API module
*
* Public API Documentation: http://getpocket.com/developer/
*
*
* Definition of keys stored in preferences to preserve user state:
* premium_status: Current premium status for logged in user if available
* Can be 0 for no premium and 1 for premium
* latestSince: Last timestamp a save happened
* tags: All tags for logged in user
* usedTags: All used tags from within the extension sorted by recency
*/
const {classes: Cc, interfaces: Ci, utils: Cu, manager: Cm} = Components;
this.EXPORTED_SYMBOLS = ["pktApi"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var pktApi = (function() {
/**
* Configuration
*/
// Base url for all api calls
var pocketAPIhost = Services.prefs.getCharPref("extensions.pocket.api"); // api.getpocket.com
var pocketSiteHost = Services.prefs.getCharPref("extensions.pocket.site"); // getpocket.com
var baseAPIUrl = "https://" + pocketAPIhost + "/v3";
/**
* Auth keys for the API requests
*/
var oAuthConsumerKey = Services.prefs.getCharPref("extensions.pocket.oAuthConsumerKey");
/**
*
*/
var prefBranch = Services.prefs.getBranch("extensions.pocket.settings.");
/**
* Helper
*/
var extend = function(out) {
out = out || {};
for (var i = 1; i < arguments.length; i++) {
if (!arguments[i])
continue;
for (var key in arguments[i]) {
if (arguments[i].hasOwnProperty(key))
out[key] = arguments[i][key];
}
}
return out;
}
var parseJSON = function(jsonString) {
try {
var o = JSON.parse(jsonString);
// Handle non-exception-throwing cases:
// Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
// but... JSON.parse(null) returns 'null', and typeof null === "object",
// so we must check for that, too.
if (o && typeof o === "object" && o !== null) {
return o;
}
}
catch (e) { }
return undefined;
};
/**
* Settings
*/
/**
* Wrapper for different plattforms to get settings for a given key
* @param {string} key A string containing the name of the key you want to
* retrieve the value of
* @return {string} String containing the value of the key. If the key
* does not exist, null is returned
*/
function getSetting(key) {
// TODO : Move this to sqlite or a local file so it's not editable (and is safer)
// https://developer.mozilla.org/en-US/Add-ons/Overlay_Extensions/XUL_School/Local_Storage
if (!prefBranch.prefHasUserValue(key))
return undefined;
return prefBranch.getComplexValue(key, Components.interfaces.nsISupportsString).data;
}
/**
* Wrapper for different plattforms to set a value for a given key in settings
* @param {string} key A string containing the name of the key you want
* to create/update.
* @param {string} value String containing the value you want to give
* the key you are creating/updating.
*/
function setSetting(key, value) {
// TODO : Move this to sqlite or a local file so it's not editable (and is safer)
// https://developer.mozilla.org/en-US/Add-ons/Overlay_Extensions/XUL_School/Local_Storage
if (!value)
prefBranch.clearUserPref(key);
else
{
// We use complexValue as tags can have utf-8 characters in them
var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
str.data = value;
prefBranch.setComplexValue(key, Components.interfaces.nsISupportsString, str);
}
}
/**
* Auth
*/
/*
* All cookies from the Pocket domain
* The return format: { cookieName:cookieValue, cookieName:cookieValue, ... }
*/
function getCookiesFromPocket() {
var cookieManager = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
var pocketCookies = cookieManager.getCookiesFromHost(pocketSiteHost, {});
var cookies = {};
while (pocketCookies.hasMoreElements()) {
var cookie = pocketCookies.getNext().QueryInterface(Ci.nsICookie2);
cookies[cookie.name] = cookie.value;
}
return cookies;
}
/**
* Returns access token or undefined if no logged in user was found
* @return {string | undefined} Access token for logged in user user
*/
function getAccessToken() {
var pocketCookies = getCookiesFromPocket();
// If no cookie was found just return undefined
if (typeof pocketCookies['ftv1'] === "undefined") {
return undefined;
}
// Check if a new user logged in in the meantime and clearUserData if so
var sessionId = pocketCookies['fsv1'];
var lastSessionId = getSetting('fsv1');
if (sessionId !== lastSessionId) {
clearUserData();
setSetting("fsv1", sessionId);
}
// Return access token
return pocketCookies['ftv1'];
}
/**
* Get the current premium status of the user
* @return {number | undefined} Premium status of user
*/
function getPremiumStatus() {
var premiumStatus = getSetting("premium_status");
if (typeof premiumStatus === "undefined") {
// Premium status is not in settings try get it from cookie
var pocketCookies = getCookiesFromPocket();
premiumStatus = pocketCookies['ps'];
}
return premiumStatus;
}
/**
* Helper method to check if a user is premium or not
* @return {Boolean} Boolean if user is premium or not
*/
function isPremiumUser() {
return getPremiumStatus() == 1;
}
/**
* Returns users logged in status
* @return {Boolean} Users logged in status
*/
function isUserLoggedIn() {
return (typeof getAccessToken() !== "undefined");
}
/**
* API
*/
/**
* Helper function for executing api requests. It mainly configures the
* ajax call with default values like type, headers or dataType for an api call.
* This function is for internal usage only.
* @param {Object} options
* Possible keys:
* - {string} path: This should be the Pocket API
* endpoint to call. For example providing the path
* "/get" would result in a call to getpocket.com/v3/get
* - {Object|undefined} data: Gets passed on to the jQuery ajax
* call as data parameter
* - {function(Object data, XMLHttpRequest xhr) | undefined} success:
* A function to be called if the request succeeds.
* - {function(Error errorThrown, XMLHttpRequest xhr) | undefined} error:
* A function to be called if the request fails.
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*
*/
function apiRequest(options) {
if ((typeof options === "undefined") || (typeof options.path === "undefined")) {
return false;
}
var url = baseAPIUrl + options.path;
var data = options.data || {};
data.locale_lang = Cc["@mozilla.org/chrome/chrome-registry;1"].
getService(Ci.nsIXULChromeRegistry).
getSelectedLocale("browser");
data.consumer_key = oAuthConsumerKey;
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Components.interfaces.nsIXMLHttpRequest);
request.open("POST", url, true);
request.onreadystatechange = function(e) {
if (request.readyState == 4) {
if (request.status === 200) {
// There could still be an error if the response is no valid json
// or does not have status = 1
var response = parseJSON(request.response);
if (options.success && response && response.status == 1) {
options.success(response, request);
return;
}
}
// Handle error case
if (options.error) {
// In case the user did revoke the access token or it's not
// valid anymore clear the user data
if (request.status === 401) {
clearUserData();
}
// Handle error message
var errorMessage;
if (request.status !== 200) {
errorMessage = request.getResponseHeader("X-Error") || request.statusText;
errorMessage = JSON.parse('"' + errorMessage + '"');
}
var error = {message: errorMessage};
options.error(error, request);
}
}
};
// Set headers
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-Accept', ' application/json');
// Serialize and Fire off the request
var str = [];
for (var p in data) {
if (data.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(data[p]));
}
}
request.send(str.join("&"));
return true;
}
/**
* Cleans all settings for the previously logged in user
*/
function clearUserData() {
// Clear stored information
setSetting("premium_status", undefined);
setSetting("latestSince", undefined);
setSetting("tags", undefined);
setSetting("usedTags", undefined);
setSetting("fsv1", undefined);
}
/**
* Add a new link to Pocket
* @param {string} url URL of the link
* @param {Object | undefined} options Can provide a string-based title, a
* `success` callback and an `error` callback.
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function addLink(url, options) {
var since = getSetting('latestSince');
var accessToken = getAccessToken();
var sendData = {
access_token: accessToken,
url: url,
since: since ? since : 0
};
if (options.title) {
sendData.title = options.title;
}
return apiRequest({
path: "/firefox/save",
data: sendData,
success: function(data) {
// Update premium status, tags and since
var tags = data.tags;
if ((typeof tags !== "undefined") && Array.isArray(tags)) {
// If a tagslist is in the response replace the tags
setSetting('tags', JSON.stringify(data.tags));
}
// Update premium status
var premiumStatus = data.premium_status;
if (typeof premiumStatus !== "undefined") {
// If a premium_status is in the response replace the premium_status
setSetting("premium_status", premiumStatus);
}
// Save since value for further requests
setSetting('latestSince', data.since);
if (options.success) {
options.success.apply(options, Array.apply(null, arguments));
}
},
error: options.error
});
}
/**
* Delete an item identified by item id from the users list
* @param {string} itemId The id from the item we want to remove
* @param {Object | undefined} options Can provide an actionInfo object with
* further data to send to the API. Can
* have success and error callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function deleteItem(itemId, options) {
var action = {
action: "delete",
item_id: itemId
};
return sendAction(action, options);
}
/**
* General function to send all kinds of actions like adding of links or
* removing of items via the API
* @param {Object} action Action object
* @param {Object | undefined} options Can provide an actionInfo object
* with further data to send to the
* API. Can have success and error
* callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function sendAction(action, options) {
// Options can have an 'actionInfo' object. This actionInfo object gets
// passed through to the action object that will be send to the API endpoint
if (typeof options.actionInfo !== 'undefined') {
action = extend(action, options.actionInfo);
}
return sendActions([action], options);
}
/**
* General function to send all kinds of actions like adding of links or
* removing of items via the API
* @param {Array} actions Array of action objects
* @param {Object | undefined} options Can have success and error callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function sendActions(actions, options) {
return apiRequest({
path: "/send",
data: {
access_token: getAccessToken(),
actions: JSON.stringify(actions)
},
success: options.success,
error: options.error
});
}
/**
* Handling Tags
*/
/**
* Add tags to the item identified by the url. Also updates the used tags
* list
* @param {string} itemId The item identifier by item id
* @param {Array} tags Tags adding to the item
* @param {Object | undefined} options Can provide an actionInfo object with
* further data to send to the API. Can
* have success and error callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function addTagsToItem(itemId, tags, options) {
return addTags({item_id: itemId}, tags, options);
}
/**
* Add tags to the item identified by the url. Also updates the used tags
* list
* @param {string} url The item identifier by url
* @param {Array} tags Tags adding to the item
* @param {Object} options Can provide an actionInfo object with further
* data to send to the API. Can have success and error
* callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function addTagsToURL(url, tags, options) {
return addTags({url: url}, tags, options);
}
/**
* Helper function to execute the add tags api call. Will be used from addTagsToURL
* and addTagsToItem but not exposed outside
* @param {string} actionPart Specific action part to add to action
* @param {Array} tags Tags adding to the item
* @param {Object | undefined} options Can provide an actionInfo object with
* further data to send to the API. Can
* have success and error callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function addTags(actionPart, tags, options) {
// Tags add action
var action = {
action: "tags_add",
tags: tags
};
action = extend(action, actionPart);
// Backup the success callback as we need it later
var finalSuccessCallback = options.success;
// Switch the success callback
options.success = function(data) {
// Update used tags
var usedTagsJSON = getSetting("usedTags");
var usedTags = usedTagsJSON ? JSON.parse(usedTagsJSON) : {};
// Check for each tag if it's already in the used tags
for (var i = 0; i < tags.length; i++) {
var tagToSave = tags[i].trim();
var newUsedTagObject = {
"tag": tagToSave,
"timestamp": new Date().getTime()
};
usedTags[tagToSave] = newUsedTagObject;
}
setSetting("usedTags", JSON.stringify(usedTags));
// Let the callback know that we are finished
if (finalSuccessCallback) {
finalSuccessCallback(data);
}
};
// Execute the action
return sendAction(action, options);
}
/**
* Get all cached tags and used tags within the callback
* @param {function(Array, Array, Boolean)} callback
* Function with tags and used tags as parameter.
*/
function getTags(callback) {
var tagsFromSettings = function() {
var tagsJSON = getSetting("tags");
if (typeof tagsJSON !== "undefined") {
return JSON.parse(tagsJSON)
}
return [];
}
var sortedUsedTagsFromSettings = function() {
// Get and Sort used tags
var usedTags = [];
var usedTagsJSON = getSetting("usedTags");
if (typeof usedTagsJSON !== "undefined") {
var usedTagsObject = JSON.parse(usedTagsJSON);
var usedTagsObjectArray = [];
for (var tagKey in usedTagsObject) {
usedTagsObjectArray.push(usedTagsObject[tagKey]);
}
// Sort usedTagsObjectArray based on timestamp
usedTagsObjectArray.sort(function(usedTagA, usedTagB) {
var a = usedTagA.timestamp;
var b = usedTagB.timestamp;
return a - b;
});
// Get all keys tags
for (var j = 0; j < usedTagsObjectArray.length; j++) {
usedTags.push(usedTagsObjectArray[j].tag);
}
// Reverse to set the last recent used tags to the front
usedTags.reverse();
}
return usedTags;
}
if (callback) {
var tags = tagsFromSettings();
var usedTags = sortedUsedTagsFromSettings();
callback(tags, usedTags);
}
}
/**
* Fetch suggested tags for a given item id
* @param {string} itemId Item id of
* @param {Object | undefined} options Can provide an actionInfo object
* with further data to send to the API.
* Can have success and error callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function getSuggestedTagsForItem(itemId, options) {
return getSuggestedTags({item_id: itemId}, options);
}
/**
* Fetch suggested tags for a given URL
* @param {string} url (required) The item identifier by url
* @param {Object} options Can provide an actionInfo object with further
* data to send to the API. Can have success and error
* callbacks
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function getSuggestedTagsForURL(url, options) {
return getSuggestedTags({url: url}, options);
}
/**
* Helper function to get suggested tags
* @return {Boolean} Returns Boolean whether the api call started sucessfully
*/
function getSuggestedTags(data, options) {
data = data || {};
options = options || {};
data.access_token = getAccessToken();
return apiRequest({
path: "/getSuggestedTags",
data: data,
success: options.success,
error: options.error
});
}
/**
* Helper function to get current signup AB group the user is in
*/
function getSignupPanelTabTestVariant() {
return getMultipleTestOption('panelSignUp', {control: 1, v1: 8, v2: 1 })
}
function getMultipleTestOption(testName, testOptions) {
// Get the test from preferences if we've already assigned the user to a test
var settingName = 'test.' + testName;
var assignedValue = getSetting(settingName);
var valArray = [];
// If not assigned yet, pick and store a value
if (!assignedValue)
{
// Get a weighted array of test variants from the testOptions object
Object.keys(testOptions).forEach(function(key) {
for (var i = 0; i < testOptions[key]; i++) {
valArray.push(key);
}
});
// Get a random test variant and set the user to it
assignedValue = valArray[Math.floor(Math.random() * valArray.length)];
setSetting(settingName, assignedValue);
}
return assignedValue;
}
/**
* Public functions
*/
return {
isUserLoggedIn : isUserLoggedIn,
clearUserData: clearUserData,
addLink: addLink,
deleteItem: deleteItem,
addTagsToItem: addTagsToItem,
addTagsToURL: addTagsToURL,
getTags: getTags,
isPremiumUser: isPremiumUser,
getSuggestedTagsForItem: getSuggestedTagsForItem,
getSuggestedTagsForURL: getSuggestedTagsForURL,
getSignupPanelTabTestVariant: getSignupPanelTabTestVariant,
};
}());

View file

@ -1,54 +0,0 @@
/* 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";
// This file is loaded as a process script, it will be loaded in the parent
// process as well as all content processes.
const { utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("chrome://pocket/content/AboutPocket.jsm");
function AboutPocketChildListener() {
}
AboutPocketChildListener.prototype = {
onStartup: function onStartup() {
// Only do this in content processes since, as the broadcaster of this
// message, the parent process doesn't also receive it. We handlers
// the shutting down separately.
if (Services.appinfo.processType ==
Services.appinfo.PROCESS_TYPE_CONTENT) {
Services.cpmm.addMessageListener("PocketShuttingDown", this, true);
}
AboutPocket.aboutSaved.register();
AboutPocket.aboutSignup.register();
},
onShutdown: function onShutdown() {
AboutPocket.aboutSignup.unregister();
AboutPocket.aboutSaved.unregister();
Services.cpmm.removeMessageListener("PocketShuttingDown", this);
Cu.unload("chrome://pocket/content/AboutPocket.jsm");
},
receiveMessage: function receiveMessage(message) {
switch (message.name) {
case "PocketShuttingDown":
this.onShutdown();
break;
default:
break;
}
return;
}
};
const listener = new AboutPocketChildListener();
listener.onStartup();

View file

@ -1,32 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>firefox@getpocket.com</em:id>
<em:version>1.0.5</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
<!-- Target Application this theme can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
<em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Pocket</em:name>
<em:description>When you find something you want to view later, put it in Pocket.</em:description>
</Description>
</RDF>

View file

@ -1,32 +0,0 @@
# 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/.
[features/firefox@getpocket.com] chrome.jar:
% content pocket %content/ contentaccessible=yes
% skin pocket classic/1.0 %skin/linux/
% skin pocket classic/1.0 %skin/osx/ os=Darwin
% skin pocket classic/1.0 %skin/windows/ os=WINNT
% skin pocket-shared classic/1.0 %skin/shared/
content/ (content/*)
skin/ (skin/*)
# windows overrides
% override chrome://pocket/skin/menuPanel.png chrome://pocket/skin/menuPanel-aero.png os=WINNT osversion=6
% override chrome://pocket/skin/menuPanel.png chrome://pocket/skin/menuPanel-aero.png os=WINNT osversion=6.1
% override chrome://pocket/skin/menuPanel@2x.png chrome://pocket/skin/menuPanel-aero@2x.png os=WINNT osversion=6
% override chrome://pocket/skin/menuPanel@2x.png chrome://pocket/skin/menuPanel-aero@2x.png os=WINNT osversion=6.1
% override chrome://pocket/skin/Toolbar@2x.png chrome://pocket/skin/Toolbar-aero@2x.png os=WINNT osversion=6
% override chrome://pocket/skin/Toolbar@2x.png chrome://pocket/skin/Toolbar-aero@2x.png os=WINNT osversion=6.1
% override chrome://pocket/skin/Toolbar@2x.png chrome://pocket/skin/Toolbar-win8@2x.png os=WINNT osversion=6.2
% override chrome://pocket/skin/Toolbar@2x.png chrome://pocket/skin/Toolbar-win8@2x.png os=WINNT osversion=6.3
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-XP.png os=WINNT osversion<6
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-aero.png os=WINNT osversion=6
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-aero.png os=WINNT osversion=6.1
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-win8.png os=WINNT osversion=6.2
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-win8.png os=WINNT osversion=6.3
# osx overrides
% override chrome://pocket/skin/Toolbar.png chrome://pocket/skin/Toolbar-yosemite.png os=Darwin osversion>=10.10
% override chrome://pocket/skin/Toolbar@2x.png chrome://pocket/skin/Toolbar-yosemite@2x.png os=Darwin osversion>=10.10
% override chrome://pocket/skin/menuPanel.png chrome://pocket/skin/menuPanel-yosemite.png os=Darwin osversion>=10.10
% override chrome://pocket/skin/menuPanel@2x.png chrome://pocket/skin/menuPanel-yosemite@2x.png os=Darwin osversion>=10.10

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Amestar etiquetes
alreadyhaveacct = ¿Yá yes un usuariu de Pocket?
continueff = Siguir con Firefox
errorgeneric = Hebo un fallu tentando de guardar en Pocket.
learnmore = Depriendi más
loginnow = Aniciar sesión
maxtaglength = Les etiquetes lléndense a 25 caráuteres
mustbeconnected = Has tar coneutáu a internet pa guardar en Pocket. Comprueba la to conexón y volvi tentalo, por favor.
onlylinkssaved = Namái puen guardase enllaces
pagenotsaved = Páxina non guardada
pageremoved = Páxina desaniciada
pagesaved = Guardóse en Pocket
processingremove = Desaniciando páxina…
processingtags = Amestando etiquetes…
removepage = Desanicia páxina
save = Guardar
saving = Guardando…
signupemail = Rexistrase con corréu
signuptosave = Rexístrate en Pocket. Ye de baldre.
suggestedtags = Etiquetes suxeríes
tagline = Guardar artículos y vídeos dende Firefox pa ver en Pocket o en cualquier preséu, en cualquier momentu.
taglinestory_one = Fai clic nel botón de Pocket pa guardar cualquier artículu, videu o páxina dende Firefox.
taglinestory_two = Ver en Pocker o en cualquier preséu, en cualquier momentu.
tagssaved = Etiquetes amestaes
tos = Sigiuiendo, tas acordies colos <a href="%1$S" target="_blank">Términos de Serviciu</a> y la <a href="%2$S" target="_blank">Política de privacidá</a> de Pocket
tryitnow = Pruébalu agora
signinfirefox = Anicia sesión con Firefox
signupfirefox = Rexístrate con Firefox
viewlist = Ver llista
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Guardar en Pocket
saveToPocketCmd.label = Guardar páxina en Pocket
saveToPocketCmd.accesskey = k
saveLinkToPocketCmd.label = Guardar enllaz en Pocket
saveLinkToPocketCmd.accesskey = o
pocketMenuitem.label = Ver la llista de Pocket

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Etiket əlavə et
alreadyhaveacct = Artıq Pocket istifadəçisisiniz?
continueff = Firefox ilə davam et
errorgeneric = Pocket-ə saxlarkən xəta baş verdi.
learnmore = Ətraflı Öyrən
loginnow = Daxil ol
maxtaglength = Etiketlər 25 simvol ilə limitlidir
mustbeconnected = Pocket-ə saxlamaq üçün internetə qoşulu olmalısınız. Lütfən internetə qoşulu olduğunuza əmin olub təkrar yoxlayın.
onlylinkssaved = Ancaq keçidlər saxlana bilər
pagenotsaved = Səhifə saxlanmadı
pageremoved = Səhifə silindi
pagesaved = Pocket-ə saxlandı
processingremove = Səhifə silinir…
processingtags = Etiketlər əlavə edilir…
removepage = Səhifəni sil
save = Saxla
saving = Saxlanır…
signupemail = E-poçt ilə qeyd ol
signuptosave = Pocket üçün qeyd ol. Bu pulsuzdur.
suggestedtags = Məsləhərli etiketlər
tagline = Firefoxdan məqalə və videoları Pocket-ə saxlayın, istədiyiniz vaxt, istədiyiniz yerdə baxın.
taglinestory_one = Firefoxda hər hansı bir məqalə, video və ya səhifəni saxlamaq üçün Pocket Düyməsinə klikləyin.
taglinestory_two = İstənilən cihazda, istənilən vaxt Pocket-də görün.
tagssaved = Etiketlər əlavə edildi
tos = Davam etməklə, Pocket-in <a href="%1$S" target="_blank">İstifadə Şərtləri</a> və <a href="%2$S" target="_blank">Məxfilik Siyasəti</a> ilə razılaşmış olursunuz
tryitnow = İndi Yoxlayın
signinfirefox = Firefox ilə daxil ol
signupfirefox = Firefox ilə qeyd ol
viewlist = Siyahını gör
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Pocket-ə Saxla
saveToPocketCmd.label = Səhifəni Pocket-ə Saxla
saveToPocketCmd.accesskey = k
saveLinkToPocketCmd.label = Keçidi Pocket-ə Saxla
saveLinkToPocketCmd.accesskey = o
pocketMenuitem.label = Pocket Siyahısını Gör

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Добавяне на етикети
alreadyhaveacct = Вече сте потребител на Pocket?
continueff = Продължаване с Firefox
errorgeneric = Получи се грешка при опит за запис в Pocket.
learnmore = Научете повече
loginnow = Вписване
maxtaglength = Етикетите могат да са до 25 знака
mustbeconnected = Трябва да сте свързан към Интернет, за да запазвате в Pocket. Моля, проверете свързаността си с Интернет и пробвайте отново.
onlylinkssaved = Могат да бъдат запазвани само връзки
pagenotsaved = Страницата не е запазена
pageremoved = Страницата е премахната
pagesaved = Запазена в Pocket
processingremove = Премахване на страница…
processingtags = Добавяне на етикети…
removepage = Премахване на страница
save = Запазване
saving = Запазване…
signupemail = Регистриране с мейл
signuptosave = Регистрирайте се в Pocket. Безплатно е.
suggestedtags = Предложени етикети
tagline = Запазвайте статии и видеота от Firefox и можете да ги преглеждате в Pocket на всяко устройство по всяко време.
taglinestory_one = Щракнете на бутона на Pocket за запазване на статия, видео или страница от Firefox.
taglinestory_two = Преглеждайте в Pocket на всяко устройство и по всяко време.
tagssaved = Етикетите са добавени
tos = Продължавайки, вие се съгласявате с <a href="%1$S" target="_blank">Условията за ползване</a> и <a href="%2$S" target="_blank">Политиката за поверителност</a> на Pocket
tryitnow = Опитайте сега
signinfirefox = Вписване с Firefox
signupfirefox = Регистриране с Firefox
viewlist = Преглед на списъка
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Запазване в Pocket
saveToPocketCmd.label = Запазване на страницата в Pocket
saveToPocketCmd.accesskey = с
saveLinkToPocketCmd.label = Запазване на връзката в Pocket
saveLinkToPocketCmd.accesskey = в
pocketMenuitem.label = Преглед списъка на Pocket

View file

@ -1,43 +0,0 @@
# 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/.
addtags = ট্যাগ যোগ করুন
alreadyhaveacct = আপনি Pocket ব্যবহার করছেন?
continueff = Firefox ব্যবহার চালিয়ে যান
errorgeneric = Pocket এ সংরক্ষণ করতে ত্রুটি ঘটেছে।
learnmore = আরও জানুন
loginnow = লগ ইন
maxtaglength = ট্যাগ ২৫ অক্ষরের মধ্যে সীমাবদ্ধ
mustbeconnected = Pocket এ কোন কিছু সংরক্ষণ করে রাখতে চাইলে, ইন্টারনেটে সংযুক্ত থাকতে হবে। ইন্টারনেট সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।
onlylinkssaved = শুধু লিঙ্ক সংরক্ষণ করা যাবে
pagenotsaved = পাতা সংরক্ষণ করা হয়নি
pageremoved = পাতা অপসারণ করা হয়েছে
pagesaved = Pocket এ সংরক্ষিত হয়েছে
processingremove = পাতা অপসারিত হচ্ছে…
processingtags = ট্যাগ যুক্ত করা হচ্ছে…
removepage = পেজ মুছে ফেলুন
save = সংরক্ষণ
saving = সংরক্ষণ করা হচ্ছে...
signupemail = ইমেইল দিয়ে সাইন আপ করুন
signuptosave = Pocket সাইন আপ করুন। এটি মুফত।
suggestedtags = প্রস্তাবিত ট্যাগ
tagline = Pocket এর মাধ্যমে যেকোন সময়, যেকোন ডিভাইসে নিবন্ধ এবং ভিডিও দেখতে Firefox থেকে সেগুলো সংরক্ষণ করুন।
taglinestory_one = Firefox থেকে আর্টিকেল, ভিডিও বা পৃষ্ঠা সংরক্ষণ করার জন্য Pocket বাটন ক্লিক করুন।
taglinestory_two = যেকোন সময়ে, যেকোন স্থানে Pocket এ দেখুন।
tagssaved = ট্যাগ যোগ করা হয়েছে
tos = এটি অব্যহত রেখে, আপনি Pocket এর <a href="%1$S" target="_blank">সেবার শর্তাবলী</a> এবং <a href="%2$S" target="_blank">গোপনীয়তা নীতিমালায়</a> সম্মত হবেন।
tryitnow = এখনই ব্যবহার করুন
signinfirefox = ফায়ারফক্স দিয়ে সাইন ইন করুন
signupfirefox = ফায়ারফক্স দিয়ে সাইন আপ করুন
viewlist = তালিকা দেখুন
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Pocket এ সংরক্ষণ করুন
saveToPocketCmd.label = Pocket এ পাতাটি সংরক্ষণ করুন k
saveToPocketCmd.accesskey = k
saveLinkToPocketCmd.label = Pocket এ লিঙ্কটি সংরক্ষণ করুন o
saveLinkToPocketCmd.accesskey = o
pocketMenuitem.label = Pocket তালিকা দেখুন

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Přidat štítky
alreadyhaveacct = Jste již uživatel služby Pocket?
continueff = Pokračovat pomocí Firefoxu
errorgeneric = Při pokusu o uložení do služby Pocket došlo k chybě.
learnmore = Zjistit více
loginnow = Přihlásit se
maxtaglength = Štítky jsou omezeny na 25 znaků
mustbeconnected = Abyste mohli ukládat do služby Pocket, musíte být připojeni k internetu. Zkontrolujte prosím své připojení a zkuste to znovu.
onlylinkssaved = Pouze odkazy mohou být uloženy
pagenotsaved = Stránka nebyla uložena
pageremoved = Stránka byla odstraněna
pagesaved = Uloženo do služby Pocket
processingremove = Odstraňování stránky…
processingtags = Přidávání štítků…
removepage = Odstranit stránku
save = Uložit
saving = Ukládání…
signupemail = Registrace e-mailem
signuptosave = Registrujte se do služby Pocket. Je to zdarma.
suggestedtags = Doporučené štítky
tagline = Ukládejte si články a videa z Firefoxu pro zobrazení ve službě Pocket kdykoliv a na jakémkoli zařízení.
taglinestory_one = Klepněte na tlačítko služby Pocket pro uložení jakéhokoliv článku, videa nebo stránky přímo z Firefoxu.
taglinestory_two = Zobrazení ve službě Pocket kdykoliv a na jakémkoliv zařízení.
tagssaved = Štítky přidány
tos = Pokračování souhlasíte s <a href="%1$S" target="_blank">Podmínkami služby</a> Pocket a <a href="%2$S" target="_blank">Zásadami ochrany osobních údajů</a>
tryitnow = Vyzkoušejte nyní
signinfirefox = Přihlášení ve Firefoxu
signupfirefox = Registrace ve Firefoxu
viewlist = Zobrazit seznam
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Uloží do služby Pocket
saveToPocketCmd.label = Uložit stránku do služby Pocket
saveToPocketCmd.accesskey = k
saveLinkToPocketCmd.label = Uložit odkaz do služby Pocket
saveLinkToPocketCmd.accesskey = o
pocketMenuitem.label = Zobrazit seznam služby Pocket

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Tilføj tags
alreadyhaveacct = Er du allerede Pocket-bruger?
continueff = Fortsæt med Firefox
errorgeneric = Der opstod en fejl ved forsøg på at gemme til Pocket.
learnmore = Læs mere
loginnow = Log ind
maxtaglength = Tags er begrænset til 25 tegn
mustbeconnected = Du skal have forbindelse til internettet for at kunne gemme til Pocket. Kontroller din internetforbindelse og prøv igen.
onlylinkssaved = Kun links kan gemmes
pagenotsaved = Siden blev ikke gemt
pageremoved = Siden er fjernet
pagesaved = Gemt til Pocket
processingremove = Fjerner side…
processingtags = Tilføjer tags…
removepage = Fjern side
save = Gem
saving = Gemmer…
signupemail = Log ind med mailadresse
signuptosave = Meld dig til Pocket. Det er gratis.
suggestedtags = Foreslåede tags
tagline = Gemmer artikler og videoer fra Firefox i Pocket, så du senere kan se dem hvor og hvornår, du har lyst.
taglinestory_one = Klik på knappen Pocket for at gemme en artikel, video eller webside fra Firefox.
taglinestory_two = Se i Pocket hvor og hvornår, du har lyst.
tagssaved = Tags tilføjet
tos = Fortsætter du, accepterer du Pockets <a href="%1$S" target="_blank">tjenestevilkår</a> og <a href="%2$S" target="_blank">privatlivspolitik</a>
tryitnow = Prøv det nu
signinfirefox = Log ind med Firefox
signupfirefox = Meld dig til med Firefox
viewlist = Vis liste
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Gem til Pocket
saveToPocketCmd.label = Gem siden til Pocket
saveToPocketCmd.accesskey = k
saveLinkToPocketCmd.label = Gem link til Pocket
saveLinkToPocketCmd.accesskey = o
pocketMenuitem.label = Vis Pocket-liste

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Tags hinzufügen
alreadyhaveacct = Sind Sie bereits Pocket-Nutzer?
continueff = Mit Firefox fortfahren
errorgeneric = Beim Speichern des Links bei Pocket ist ein Fehler aufgetreten.
learnmore = Mehr erfahren
loginnow = Anmelden
maxtaglength = Tags dürfen höchsten 25 Zeichen lang sein.
mustbeconnected = Bitte überprüfen Sie, ob Sie mit dem Internet verbunden sind.
onlylinkssaved = Es können nur Links gespeichert werden
pagenotsaved = Seite nicht gespeichert
pageremoved = Seite entfernt
pagesaved = Bei Pocket gespeichert
processingremove = Seite wird entfernt…
processingtags = Tags werden hinzugefügt…
removepage = Seite entfernen
save = Speichern
saving = Speichern…
signupemail = Mit E-Mail registrieren
signuptosave = Registrieren Sie sich bei Pocket. Das ist kostenlos.
suggestedtags = Vorgeschlagene Tags
tagline = Speichern Sie Artikel und Videos aus Firefox bei Pocket, um sie jederzeit und auf jedem Gerät ansehen zu können.
taglinestory_one = Klicken Sie auf die Pocket-Schaltfläche, um beliebige Artikel, Videos und Seiten aus Firefox zu speichern.
taglinestory_two = Lesen Sie diese mit Pocket, jederzeit und auf jedem Gerät.
tagssaved = Tags hinzugefügt
tos = Indem Sie fortfahren, akzeptieren Sie die <a href="%1$S" target="_blank">Nutzungsbedingungen</a> und die <a href="%2$S" target="_blank">Datenschutzerklärung</a> von Pocket.
tryitnow = Jetzt ausprobieren
signinfirefox = Mit Firefox anmelden
signupfirefox = Mit Firefox registrieren
viewlist = Liste anzeigen
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Bei Pocket speichern
saveToPocketCmd.label = Seite bei Pocket speichern
saveToPocketCmd.accesskey = b
saveLinkToPocketCmd.label = Link bei Pocket speichern
saveLinkToPocketCmd.accesskey = c
pocketMenuitem.label = Pocket-Liste anzeigen

View file

@ -1,43 +0,0 @@
# 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/.
addtags = Wobznamjenja pśidaś
alreadyhaveacct = Sćo južo wužywaŕ Pocket?
continueff = Z Firefox pókšacowaś
errorgeneric = Pśi składowanju do Pocket jo zmólka nastała.
learnmore = Dalšne informacije
loginnow = Pśizjawiś
maxtaglength = Wobznamjenja su na 25 znamuškow wobgranicowane
mustbeconnected = Musyśo z internetom zwězany byś, aby do Pocket składował. Pšosym pśeglědajśo swój zwisk a wopytajśo hyšći raz.
onlylinkssaved = Jano wótkaze daju se składowaś
pagenotsaved = Bok njejo se składł
pageremoved = Bok jo se wótwónoźeł
pagesaved = Do Pocket skłaźony
processingremove = Bok se wótwónoźujo…
processingtags = Wobznamjenja se pśidawaju…
removepage = Bok wótwónoźeś
save = Składowaś
saving = Składujo se…
signupemail = Registrěrujśo se z mejlku
signuptosave = Registrěrujśo se za Pocket. Jo dermo.
suggestedtags = Naraźone wobznamjenja
tagline = Składujśo nastawki a wideo z Firefox, aby se je kuždy cas w Pocket na kuždem rěźe woglědał.
taglinestory_one = Klikniśo na tłocašk Pocket, aby nastawk, wideo abo bok z Firefox składował.
taglinestory_two = Se w Pocket na kuždem rěźee kuždy cas woglědaś.
tagssaved = Wobznamjenja su se pśidali
tos = Gaž pókšacujośo, zwólijośo do <a href="%1$S" target="_blank">wužywarskich wuměnjenjow</a> a <a href="%2$S" target="_blank">pšawidłow priwatnosći</a> Pocket
tryitnow = Wopytajśo to něnto
signinfirefox = Z Firefox pśizjawiś
signupfirefox = Z Firefox registrěrowaś
viewlist = Lisćinu pokazaś
# LOCALIZATION NOTE(pocket-button.label, pocket-button.tooltiptext, saveToPocketCmd.label, saveLinkToPocketCmd.label, pocketMenuitem.label):
# "Pocket" is a brand name.
pocket-button.label = Pocket
pocket-button.tooltiptext = Do Pocket składowaś
saveToPocketCmd.label = Bok do Pocket składowaś
saveToPocketCmd.accesskey = b
saveLinkToPocketCmd.label = Wótkaz do Pocket składowaś
saveLinkToPocketCmd.accesskey = w
pocketMenuitem.label = Lisćinu Pocket pokazaś

Some files were not shown because too many files have changed in this diff Show more