Merge f-t to m-c, a=merge
@ -29,16 +29,8 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.sdk.deprecated += [
|
||||
'source/lib/sdk/deprecated/api-utils.js',
|
||||
'source/lib/sdk/deprecated/cortex.js',
|
||||
'source/lib/sdk/deprecated/errors.js',
|
||||
'source/lib/sdk/deprecated/events.js',
|
||||
'source/lib/sdk/deprecated/light-traits.js',
|
||||
'source/lib/sdk/deprecated/list.js',
|
||||
'source/lib/sdk/deprecated/memory.js',
|
||||
'source/lib/sdk/deprecated/symbiont.js',
|
||||
'source/lib/sdk/deprecated/sync-worker.js',
|
||||
'source/lib/sdk/deprecated/traits-worker.js',
|
||||
'source/lib/sdk/deprecated/traits.js',
|
||||
'source/lib/sdk/deprecated/unit-test-finder.js',
|
||||
'source/lib/sdk/deprecated/unit-test.js',
|
||||
'source/lib/sdk/deprecated/window-utils.js',
|
||||
@ -91,7 +83,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
|
||||
'source/lib/sdk/test/memory.js',
|
||||
'source/lib/sdk/test/options.js',
|
||||
'source/lib/sdk/test/runner.js',
|
||||
'source/lib/sdk/test/tmp-file.js',
|
||||
'source/lib/sdk/test/utils.js',
|
||||
]
|
||||
|
||||
@ -128,12 +119,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk":
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.sdk.windows += [
|
||||
'source/lib/sdk/windows/dom.js',
|
||||
'source/lib/sdk/windows/fennec.js',
|
||||
'source/lib/sdk/windows/firefox.js',
|
||||
'source/lib/sdk/windows/observer.js',
|
||||
'source/lib/sdk/windows/tabs-fennec.js',
|
||||
'source/lib/sdk/windows/tabs-firefox.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs += [
|
||||
@ -183,10 +172,22 @@ EXTRA_JS_MODULES.commonjs.framescript += [
|
||||
'source/lib/framescript/util.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs['jetpack-id'] += [
|
||||
'source/lib/jetpack-id/index.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.method += [
|
||||
'source/lib/method/core.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs['mozilla-toolkit-versioning'] += [
|
||||
'source/lib/mozilla-toolkit-versioning/index.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs['mozilla-toolkit-versioning'].lib += [
|
||||
'source/lib/mozilla-toolkit-versioning/lib/utils.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.node += [
|
||||
'source/lib/node/os.js',
|
||||
]
|
||||
@ -218,7 +219,6 @@ EXTRA_JS_MODULES.commonjs.sdk += [
|
||||
'source/lib/sdk/timers.js',
|
||||
'source/lib/sdk/ui.js',
|
||||
'source/lib/sdk/url.js',
|
||||
'source/lib/sdk/widget.js',
|
||||
'source/lib/sdk/windows.js',
|
||||
]
|
||||
|
||||
@ -277,10 +277,6 @@ EXTRA_JS_MODULES.commonjs.sdk.deprecated.events += [
|
||||
'source/lib/sdk/deprecated/events/assembler.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.sdk.deprecated.traits += [
|
||||
'source/lib/sdk/deprecated/traits/core.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.sdk.dom += [
|
||||
'source/lib/sdk/dom/events.js',
|
||||
]
|
||||
@ -306,7 +302,6 @@ EXTRA_JS_MODULES.commonjs.sdk.input += [
|
||||
'source/lib/sdk/input/customizable-ui.js',
|
||||
'source/lib/sdk/input/frame.js',
|
||||
'source/lib/sdk/input/system.js',
|
||||
'source/lib/sdk/input/window.js',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.commonjs.sdk.io += [
|
||||
|
16
addon-sdk/source/.gitignore
vendored
@ -9,6 +9,7 @@ doc/modules/
|
||||
doc/status.md5
|
||||
packages/*
|
||||
node_modules
|
||||
cache
|
||||
|
||||
# Python
|
||||
*.pyc
|
||||
@ -18,3 +19,18 @@ node_modules
|
||||
|
||||
# Windows
|
||||
*Thumbs.db
|
||||
|
||||
# Ignore subtrees
|
||||
|
||||
# git@github.com:jsantell/jetpack-id.git
|
||||
lib/jetpack-id/*
|
||||
!lib/jetpack-id/index.js
|
||||
!lib/jetpack-id/package.json
|
||||
|
||||
# git@github.com:jsantell/mozilla-toolkit-versioning.git
|
||||
lib/mozilla-toolkit-versioning/*
|
||||
!lib/mozilla-toolkit-versioning/index.js
|
||||
!lib/mozilla-toolkit-versioning/lib
|
||||
lib/mozilla-toolkit-versioning/lib/*
|
||||
!lib/mozilla-toolkit-versioning/lib/*.js
|
||||
!lib/mozilla-toolkit-versioning/package.json
|
||||
|
@ -8,6 +8,7 @@ bin/
|
||||
modules/
|
||||
node_modules/
|
||||
examples/
|
||||
cache/
|
||||
|
||||
# Python
|
||||
python-lib/
|
||||
|
@ -1,24 +1,26 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "iojs"
|
||||
- "0.10"
|
||||
|
||||
env:
|
||||
- JPM_FX_DEBUG=0
|
||||
- JPM_FX_DEBUG=1
|
||||
|
||||
notifications:
|
||||
irc: "irc.mozilla.org#jetpack"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- cache
|
||||
|
||||
before_install:
|
||||
- "export DISPLAY=:99.0"
|
||||
- "sh -e /etc/init.d/xvfb start"
|
||||
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 -extension RANDR"
|
||||
|
||||
before_script:
|
||||
- npm install mozilla-download -g
|
||||
- npm install jpm -g
|
||||
- cd ..
|
||||
- mozilla-download --branch nightly -c prerelease --host ftp.mozilla.org firefox
|
||||
- npm install fx-download -g
|
||||
- npm install gulp -g
|
||||
- bash bin/fx-download.sh
|
||||
- export JPM_FIREFOX_BINARY=$TRAVIS_BUILD_DIR/../firefox/firefox
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
|
||||
script:
|
||||
- npm test
|
||||
|
@ -1,19 +1,19 @@
|
||||
## Overview
|
||||
|
||||
- Changes should follow the [design guidelines], as well as the [coding style guide]
|
||||
- All changes must be accompanied by tests
|
||||
- In order to land, changes must have been reviewed by one of the Jetpack reviewers
|
||||
- Changes should have additional API review when needed
|
||||
- Changes should have additional review from a Mozilla platform domain-expert when needed
|
||||
- All changes need tests
|
||||
- In order to land, changes need a review by one of the Jetpack reviewers
|
||||
- Changes may need an API review
|
||||
- Changes may need a review from a Mozilla platform domain-expert
|
||||
|
||||
If you have questions, ask in [#jetpack on IRC][jetpack irc channel] or on the [Jetpack mailing list].
|
||||
|
||||
## How to Make Code Contributions
|
||||
|
||||
If you'd like to contribute the Jetpack project, follow these steps:
|
||||
To write code for the Jetpack project, follow these steps:
|
||||
|
||||
1. Look for your issue in the list of [bugs already filed][open bugs]. If you want to contribute, but don't already know what you want to do, we keep a list of [good first bugs].
|
||||
2. If no bug exists, [submit one][submit bug].
|
||||
1. Look for your issue in the list of [bugs already filed][open bugs]. If you don't already know what you want to do, we keep a list of [good first bugs].
|
||||
2. If no bug exists, [make one][make bug].
|
||||
3. Get the code: get a [GitHub][GitHub] account, fork the [Add-on SDK repo][Add-on SDK repo], and clone it to your machine.
|
||||
4. Make your changes. Changes should follow the [design guidelines] as well as the [coding style guide].
|
||||
5. Write tests: [unit testing introduction][test intro], [unit testing API][test API].
|
||||
@ -24,7 +24,7 @@ If you'd like to contribute the Jetpack project, follow these steps:
|
||||
10. Flag the attachment for code review from one of the Jetpack reviewers listed below. This step is optional, but could speed things up.
|
||||
11. Address any issues mentioned in the review.
|
||||
|
||||
Finally, once review is approved, a team member will do the merging
|
||||
Finally, once the review is positive, a team member will do the merging
|
||||
|
||||
## Good First Bugs
|
||||
|
||||
@ -32,7 +32,7 @@ There is a list of [good first bugs here][good first bugs].
|
||||
|
||||
## Reviewers
|
||||
|
||||
All changes must be reviewed by someone on the Jetpack review crew:
|
||||
All changes need a review by someone on the Jetpack review crew:
|
||||
|
||||
- [@mossop]
|
||||
- [@gozala]
|
||||
@ -50,7 +50,7 @@ For API and developer ergonomics review, ask [@gozala].
|
||||
[jetpack irc channel]:irc://irc.mozilla.org/#jetpack
|
||||
[Jetpack mailing list]:http://groups.google.com/group/mozilla-labs-jetpack
|
||||
[open bugs]:https://bugzilla.mozilla.org/buglist.cgi?quicksearch=product%3ASDK
|
||||
[submit bug]:https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK&component=general
|
||||
[make bug]:https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK&component=general
|
||||
[test intro]:https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Unit_testing
|
||||
[test API]:https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/test_assert
|
||||
[coding style guide]:https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
|
||||
|
@ -6,7 +6,7 @@ If you find a problem, please [report the bug here](https://bugzilla.mozilla.org
|
||||
|
||||
## Developing Add-ons
|
||||
|
||||
These resources should provide some help:
|
||||
These resources offer some help:
|
||||
|
||||
* [Add-on SDK Documentation](https://developer.mozilla.org/en-US/Add-ons/SDK)
|
||||
* [Community Developed Modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules)
|
||||
@ -17,7 +17,7 @@ These resources should provide some help:
|
||||
|
||||
## Contributing Code
|
||||
|
||||
Please read these two guides if you wish to contribute some patches to the addon-sdk:
|
||||
Please read these two guides if you wish to make some patches to the addon-sdk:
|
||||
|
||||
* [Contribute Guide](https://github.com/mozilla/addon-sdk/blob/master/CONTRIBUTING.md)
|
||||
* [Style Guide](https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide)
|
||||
@ -29,3 +29,4 @@ We use [bugzilla](https://bugzilla.mozilla.org/) as our issue tracker, here are
|
||||
* [File a bug](https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK)
|
||||
* [Open bugs](https://bugzilla.mozilla.org/buglist.cgi?bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&columnlist=bug_severity%2Cpriority%2Cassigned_to%2Cbug_status%2Ctarget_milestone%2Cresolution%2Cshort_desc%2Cchangeddate&product=Add-on%20SDK&query_format=advanced&order=priority)
|
||||
* [Good first bugs](https://bugzilla.mozilla.org/buglist.cgi?status_whiteboard=[good+first+bug]&&resolution=---&product=Add-on+SDK)
|
||||
* [Good next bugs](https://bugzilla.mozilla.org/buglist.cgi?status_whiteboard=[good+next+bug]&&resolution=---&product=Add-on+SDK)
|
||||
|
7
addon-sdk/source/bin/fx-download.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ "$JPM_FX_DEBUG" = "1" ]; then
|
||||
fx-download --branch nightly -c prerelease --host ftp.mozilla.org ../firefox --debug
|
||||
else
|
||||
fx-download --branch nightly -c prerelease --host ftp.mozilla.org ../firefox
|
||||
fi
|
0
addon-sdk/source/bin/integration-scripts/buildbot-run-cfx-helper
Normal file → Executable file
@ -11,13 +11,17 @@ var mocha = new Mocha({
|
||||
timeout: 900000
|
||||
});
|
||||
|
||||
var isDebug = require("./node-scripts/utils").isDebug;
|
||||
|
||||
exports.run = function(type) {
|
||||
return new Promise(function(resolve) {
|
||||
type = type || "";
|
||||
[
|
||||
(/^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
|
||||
(/^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
|
||||
(!isDebug && /^(modules)?$/.test(type)) && require.resolve("../bin/node-scripts/test.modules"),
|
||||
(!isDebug && /^(addons)?$/.test(type)) && require.resolve("../bin/node-scripts/test.addons"),
|
||||
(/^(examples)?$/.test(type)) && require.resolve("../bin/node-scripts/test.examples"),
|
||||
(!isDebug && /^(docs)?$/.test(type)) && require.resolve("../bin/node-scripts/test.docs"),
|
||||
(!isDebug && /^(ini)?$/.test(type)) && require.resolve("../bin/node-scripts/test.ini"),
|
||||
].sort().forEach(function(filepath) {
|
||||
filepath && mocha.addFile(filepath);
|
||||
})
|
||||
|
64
addon-sdk/source/bin/node-scripts/apply-patch.js
Normal file
@ -0,0 +1,64 @@
|
||||
/* 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";
|
||||
|
||||
var path = require("path");
|
||||
var cp = require("child_process");
|
||||
var fs = require("fs");
|
||||
var Promise = require("promise");
|
||||
var patcher = require("patch-editor");
|
||||
var readParam = require("./utils").readParam;
|
||||
|
||||
var isKeeper = /\/addon-sdk\/source/;
|
||||
|
||||
function apply(options) {
|
||||
return clean(options).then(function() {
|
||||
return new Promise(function(resolve) {
|
||||
var patch = path.resolve(readParam("patch"));
|
||||
var proc = cp.spawn("git", ["apply", patch]);
|
||||
proc.stdout.pipe(process.stdout);
|
||||
proc.stderr.pipe(process.stderr);
|
||||
proc.on("close", resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.apply = apply;
|
||||
|
||||
function clean(options) {
|
||||
return new Promise(function(resolve) {
|
||||
var patch = path.resolve(readParam("patch"));
|
||||
if (!patch) {
|
||||
throw new Error("no --patch was provided.");
|
||||
}
|
||||
console.log("Cleaning patch " + patch);
|
||||
|
||||
patcher.getChunks({ patch: patch }).then(function(chunks) {
|
||||
var keepers = [];
|
||||
|
||||
for (var i = chunks.length - 1; i >= 0; i--) {
|
||||
var chunk = chunks[i];
|
||||
var files = chunk.getFilesChanged();
|
||||
|
||||
// check if the file changed is related to the addon-sdk/source directory
|
||||
var keepIt = files.map(function(file) {
|
||||
return (isKeeper.test(file));
|
||||
}).reduce(function(prev, curr) {
|
||||
return prev || curr;
|
||||
}, false);
|
||||
|
||||
if (keepIt) {
|
||||
keepers.push(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
var contents = "\n" + keepers.join("\n") + "\n";
|
||||
contents = contents.replace(/\/addon-sdk\/source/g, "");
|
||||
|
||||
fs.writeFileSync(patch, contents, { encoding: "utf8" });
|
||||
|
||||
console.log("Done cleaning patch.");
|
||||
}).then(resolve).catch(console.error);
|
||||
});
|
||||
}
|
||||
exports.clean = clean;
|
@ -8,6 +8,7 @@ var path = require("path");
|
||||
var fs = require("fs");
|
||||
var jpm = utils.run;
|
||||
var readParam = utils.readParam;
|
||||
var isDebug = utils.isDebug;
|
||||
|
||||
var addonsPath = path.join(__dirname, "..", "..", "test", "addons");
|
||||
|
||||
@ -40,6 +41,14 @@ function fileFilter(root, file) {
|
||||
if (/^(l10n-properties|simple-prefs|page-mod-debugger)/.test(file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter additional add-ons when using debug builds
|
||||
if (isDebug) {
|
||||
if (/^(chrome|e10s)/.test(file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (matcher && !matcher.test(file)) {
|
||||
return false;
|
||||
}
|
||||
|
145
addon-sdk/source/bin/node-scripts/test.docs.js
Normal file
@ -0,0 +1,145 @@
|
||||
/* 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";
|
||||
|
||||
var createHash = require('crypto').createHash;
|
||||
var fs = require("fs");
|
||||
var fsExtra = require("fs-extra")
|
||||
var path = require("path");
|
||||
var Promise = require("promise");
|
||||
var chai = require("chai");
|
||||
var expect = chai.expect;
|
||||
var teacher = require("teacher");
|
||||
|
||||
var rootURI = path.join(__dirname, "..", "..");
|
||||
|
||||
// get a list of words that fail spell check but are still acceptable
|
||||
var NEW_WORDS = fs.readFileSync(path.join(__dirname, "words.txt")).toString().trim().split("\n");
|
||||
|
||||
var CACHE_PATH = path.join(__dirname, "..", "..", "cache", "spellchecks.json");
|
||||
|
||||
var CACHE = {};
|
||||
|
||||
try {
|
||||
CACHE = JSON.parse(fs.readFileSync(CACHE_PATH).toString());
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
function md5(str) {
|
||||
return createHash("md5").update(str).digest("utf8");
|
||||
}
|
||||
|
||||
function addCacheHash(hash) {
|
||||
CACHE[hash] = true;
|
||||
fsExtra.ensureFileSync(CACHE_PATH);
|
||||
fsExtra.writeJSONSync(CACHE_PATH, CACHE);
|
||||
}
|
||||
|
||||
describe("Spell Checking", function () {
|
||||
it("Spellcheck CONTRIBUTING.md", function (done) {
|
||||
var readme = path.join(rootURI, "CONTRIBUTING.md");
|
||||
|
||||
fs.readFile(readme, function (err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
var text = data.toString();
|
||||
var hash = md5(text);
|
||||
|
||||
// skip this test if we know we have done the
|
||||
// exact same test with positive results before
|
||||
if (CACHE[hash]) {
|
||||
expect(CACHE[hash]).to.be.equal(true);
|
||||
return done();
|
||||
}
|
||||
|
||||
teacher.check(text, function(err, data) {
|
||||
expect(err).to.be.equal(null);
|
||||
|
||||
var results = data || [];
|
||||
results = results.filter(function(result) {
|
||||
if (NEW_WORDS.indexOf(result.string.toLowerCase()) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore anything that starts with a dash
|
||||
if (result.string[0] == "-") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(new RegExp(result.string)).test(text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
if (results.length > 0) {
|
||||
console.log(results);
|
||||
}
|
||||
else {
|
||||
addCacheHash(hash);
|
||||
}
|
||||
|
||||
expect(results.length).to.be.equal(0);
|
||||
|
||||
setTimeout(done, 500);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("Spellcheck README.md", function (done) {
|
||||
var readme = path.join(rootURI, "README.md");
|
||||
|
||||
fs.readFile(readme, function (err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
var text = data.toString();
|
||||
var hash = md5(text);
|
||||
|
||||
// skip this test if we know we have done the
|
||||
// exact same test with positive results before
|
||||
if (CACHE[hash]) {
|
||||
expect(CACHE[hash]).to.be.equal(true);
|
||||
return done();
|
||||
}
|
||||
|
||||
teacher.check(text, function(err, data) {
|
||||
expect(err).to.be.equal(null);
|
||||
|
||||
var results = data || [];
|
||||
results = results.filter(function(result) {
|
||||
if (NEW_WORDS.indexOf(result.string.toLowerCase()) != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore anything that starts with a dash
|
||||
if (result.string[0] == "-") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ignore anything that we don't find in the original text,
|
||||
// for some reason "bootstrap.js" becomes "bootstrapjs".
|
||||
if (!(new RegExp(result.string)).test(text)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
|
||||
if (results.length > 0) {
|
||||
console.log(results);
|
||||
}
|
||||
else {
|
||||
addCacheHash(hash);
|
||||
}
|
||||
|
||||
expect(results.length).to.be.equal(0);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
39
addon-sdk/source/bin/node-scripts/test.ini.js
Normal file
@ -0,0 +1,39 @@
|
||||
/* 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";
|
||||
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var Promise = require("promise");
|
||||
var chai = require("chai");
|
||||
var expect = chai.expect;
|
||||
var ini = require("./update-ini");
|
||||
|
||||
var addonINI = path.resolve("./test/addons/jetpack-addon.ini");
|
||||
|
||||
describe("Checking ini files", function () {
|
||||
|
||||
it("Check test/addons/jetpack-addon.ini", function (done) {
|
||||
|
||||
fs.readFile(addonINI, function (err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
var text = data.toString();
|
||||
var expected = "";
|
||||
|
||||
ini.makeAddonIniContent()
|
||||
.then(function(contents) {
|
||||
expected = contents;
|
||||
|
||||
setTimeout(function end() {
|
||||
expect(expected.trim()).to.be.equal(text.trim());
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
58
addon-sdk/source/bin/node-scripts/update-ini.js
Normal file
@ -0,0 +1,58 @@
|
||||
/* 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";
|
||||
|
||||
var path = require("path");
|
||||
var cp = require("child_process");
|
||||
var fs = require("fs");
|
||||
var Promise = require("promise");
|
||||
var parser = require("ini-parser");
|
||||
|
||||
var addonINI = path.resolve("./test/addons/jetpack-addon.ini");
|
||||
var addonsDir = path.resolve("./test/addons/");
|
||||
|
||||
function updateAddonINI() {
|
||||
return new Promise(function(resolve) {
|
||||
console.log("Start updating " + addonINI);
|
||||
|
||||
makeAddonIniContent().
|
||||
then(function(contents) {
|
||||
fs.writeFileSync(addonINI, contents, { encoding: "utf8" });
|
||||
console.log("Done updating " + addonINI);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
}
|
||||
exports.updateAddonINI = updateAddonINI;
|
||||
|
||||
function makeAddonIniContent() {
|
||||
return new Promise(function(resolve) {
|
||||
var data = parser.parse(fs.readFileSync(addonINI, { encoding: "utf8" }).toString());
|
||||
var result = {};
|
||||
|
||||
fs.readdir(addonsDir, function(err, files) {
|
||||
var folders = files.filter(function(file) {
|
||||
return fs.statSync(path.resolve(addonsDir, file)).isDirectory();
|
||||
}).sort();
|
||||
|
||||
folders.forEach(function(folder) {
|
||||
var oldData = data[folder + ".xpi"];
|
||||
result[folder] = oldData ? oldData : {};
|
||||
});
|
||||
|
||||
// build ini file
|
||||
var contents = [];
|
||||
Object.keys(result).sort().forEach(function(key) {
|
||||
contents.push("[" + key + ".xpi]");
|
||||
Object.keys(result[key]).forEach(function(dataKey) {
|
||||
contents.push(dataKey + " = " + result[key][dataKey]);
|
||||
});
|
||||
});
|
||||
contents = contents.join("\n") + "\n";
|
||||
|
||||
return resolve(contents);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.makeAddonIniContent = makeAddonIniContent;
|
@ -17,13 +17,25 @@ var sdk = path.join(__dirname, "..", "..");
|
||||
var prefsPath = path.join(sdk, "test", "preferences", "test-preferences.js");
|
||||
var e10sPrefsPath = path.join(sdk, "test", "preferences", "test-e10s-preferences.js");
|
||||
|
||||
var OUTPUT_FILTERS = [
|
||||
/[^\n\r]+WARNING\: NS_ENSURE_SUCCESS\(rv, rv\) failed[^\n]+\n\r?/
|
||||
];
|
||||
|
||||
var isDebug = (process.env["JPM_FX_DEBUG"] == "1");
|
||||
exports.isDebug = isDebug;
|
||||
|
||||
function spawn (cmd, options) {
|
||||
options = options || {};
|
||||
var env = _.extend({}, options.env, process.env);
|
||||
|
||||
if (isDebug) {
|
||||
env["MOZ_QUIET"] = 1;
|
||||
}
|
||||
|
||||
var e10s = options.e10s || false;
|
||||
|
||||
return child_process.spawn("node", [
|
||||
jpm, cmd, "-v",
|
||||
jpm, cmd, "-v", "--tbpl",
|
||||
"--prefs", e10s ? e10sPrefsPath : prefsPath,
|
||||
"-o", sdk,
|
||||
"-f", options.filter || ""
|
||||
@ -37,16 +49,34 @@ exports.spawn = spawn;
|
||||
function run (cmd, options, p) {
|
||||
return new Promise(function(resolve) {
|
||||
var output = [];
|
||||
|
||||
var proc = spawn(cmd, options);
|
||||
proc.stderr.pipe(process.stderr);
|
||||
proc.stdout.on("data", function (data) {
|
||||
for (var i = OUTPUT_FILTERS.length - 1; i >= 0; i--) {
|
||||
if (OUTPUT_FILTERS[i].test(data)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
output.push(data);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (p) {
|
||||
proc.stdout.pipe(p.stdout);
|
||||
}
|
||||
else {
|
||||
proc.stdout.on("data", function (data) {
|
||||
data = (data || "") + "";
|
||||
if (/TEST-/.test(data)) {
|
||||
DEFAULT_PROCESS.stdout.write(data.replace(/[\s\n]+$/, "") + "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
proc.on("close", function(code) {
|
||||
var out = output.join("");
|
||||
var buildDisplayed = /Build \d+/.test(out);
|
||||
var noTests = /No tests were run/.test(out);
|
||||
var hasSuccess = /All tests passed!/.test(out);
|
||||
var hasFailure = /There were test failures\.\.\./.test(out);
|
||||
@ -54,6 +84,7 @@ function run (cmd, options, p) {
|
||||
DEFAULT_PROCESS.stdout.write(out);
|
||||
}
|
||||
expect(code).to.equal(hasFailure ? 1 : 0);
|
||||
expect(buildDisplayed).to.equal(true);
|
||||
expect(hasFailure).to.equal(false);
|
||||
expect(hasSuccess).to.equal(true);
|
||||
expect(noTests).to.equal(false);
|
||||
|
12
addon-sdk/source/bin/node-scripts/words.txt
Normal file
@ -0,0 +1,12 @@
|
||||
addon-sdk
|
||||
github
|
||||
stackoverflow
|
||||
bugzilla
|
||||
irc
|
||||
erikvold
|
||||
jsantell
|
||||
mossop
|
||||
gozala
|
||||
zer0
|
||||
autonome
|
||||
0c0w3
|
@ -6,5 +6,5 @@
|
||||
"version": "0.0.1",
|
||||
"author": "Irakli Gozalishvili",
|
||||
"main": "./index.js",
|
||||
"license": "MPL 2.0"
|
||||
"license": "MPL-2.0"
|
||||
}
|
||||
|
@ -1,46 +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/. -->
|
||||
|
||||
This add-on enables users to add notes, or annotations, to Web pages.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To switch the annotator on, left-click the pencil icon in the Add-on Bar. The
|
||||
icon should turn yellow: this indicates that the annotator is active. To switch
|
||||
it off, click it again. Switching it on/off only stops you from entering
|
||||
annotations: existing annotations are still displayed.
|
||||
|
||||
When the annotator is active and the user moves the mouse over a page element
|
||||
that can be annotated, the annotator highlights that elements by giving it a
|
||||
yellow background.
|
||||
|
||||
If the user clicks on a highlighted element the add-on opens a dialog for the
|
||||
user to enter the annotation. When the user hits <return> the annotation is
|
||||
saved.
|
||||
|
||||
Elements which have been annotated are displayed with a yellow border: when the
|
||||
user moves the mouse over one of these elements, the add-on displays the
|
||||
annotation associated with that element.
|
||||
|
||||
To view all annotations in a list, right-click the pencil icon.
|
||||
|
||||
The add-on is deactivated in private browsing mode, meaning that new annotations
|
||||
can't be created although existing ones are still shown. On exiting private
|
||||
browsing the add-on returns to its previous activation state.
|
||||
|
||||
Known Issues/Limitations
|
||||
------------------------
|
||||
|
||||
It is not possible to delete annotations, or to edit them after creating them,
|
||||
but it would be simple to add this.
|
||||
|
||||
When right-clicking the annotator icon the add-on bar's context-menu is shown:
|
||||
this is tracked by
|
||||
[bug 626326](https://bugzilla.mozilla.org/show_bug.cgi?id=626326).
|
||||
|
||||
The list of annotations should be anchored to the widget. The annotation
|
||||
editor, and the annotation itself, should be anchored to the element which is
|
||||
annotated. The will be done when the implementation of panel-anchoring is
|
||||
extended.
|
@ -1,31 +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/. -->
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Annotation</title>
|
||||
<style type="text/css" media="all">
|
||||
|
||||
body {
|
||||
font: 100% arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id = "annotation">
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,11 +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/. */
|
||||
|
||||
/*
|
||||
Initialize annotation content.
|
||||
*/
|
||||
|
||||
self.on('message', function(message) {
|
||||
$('#annotation').text(message);
|
||||
});
|
@ -1,39 +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/. -->
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||
<head>
|
||||
<title>Annotation</title>
|
||||
<style type="text/css" media="all">
|
||||
body, html {
|
||||
font: 100% arial, helvetica, sans-serif;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
color: inherit !important;
|
||||
font: inherit !important;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<textarea rows='10' cols='20' id='annotation-box'>
|
||||
</textarea>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,23 +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/. */
|
||||
|
||||
/*
|
||||
On a return key, send the content of the textArea back to the add-on,
|
||||
and zero the textArea for the next time.
|
||||
*/
|
||||
|
||||
var textArea = document.getElementById('annotation-box');
|
||||
|
||||
textArea.onkeyup = function(event) {
|
||||
if (event.keyCode == 13) {
|
||||
self.postMessage(textArea.value);
|
||||
textArea.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
self.on('message', function() {
|
||||
var textArea = document.getElementById('annotation-box');
|
||||
textArea.value = '';
|
||||
textArea.focus();
|
||||
});
|
@ -1,154 +0,0 @@
|
||||
/*!
|
||||
* jQuery JavaScript Library v1.4.2
|
||||
* http://jquery.com/
|
||||
*
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* http://sizzlejs.com/
|
||||
* Copyright 2010, The Dojo Foundation
|
||||
* Released under the MIT, BSD, and GPL Licenses.
|
||||
*
|
||||
* Date: Sat Feb 13 22:33:48 2010 -0500
|
||||
*/
|
||||
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
|
||||
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
|
||||
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
|
||||
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
|
||||
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
|
||||
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
|
||||
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
|
||||
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
|
||||
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
|
||||
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
|
||||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
|
||||
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
|
||||
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
|
||||
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
|
||||
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
|
||||
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
|
||||
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
|
||||
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
||||
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
|
||||
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
|
||||
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
|
||||
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
|
||||
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
|
||||
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
|
||||
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
|
||||
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
|
||||
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
|
||||
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
|
||||
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
|
||||
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
|
||||
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
|
||||
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
|
||||
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
|
||||
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
|
||||
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
|
||||
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
|
||||
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
|
||||
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
|
||||
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
|
||||
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
|
||||
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
|
||||
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
|
||||
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
|
||||
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
|
||||
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
|
||||
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
|
||||
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
|
||||
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
|
||||
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
|
||||
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
|
||||
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
|
||||
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
||||
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
|
||||
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
|
||||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
|
||||
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
|
||||
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
||||
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
|
||||
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
|
||||
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
|
||||
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
|
||||
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
|
||||
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
|
||||
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
|
||||
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
|
||||
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
|
||||
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
|
||||
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
|
||||
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
|
||||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
|
||||
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
|
||||
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
|
||||
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
|
||||
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
|
||||
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
|
||||
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
|
||||
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
|
||||
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
|
||||
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
|
||||
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
|
||||
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
|
||||
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
|
||||
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
|
||||
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
|
||||
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
|
||||
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
|
||||
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
|
||||
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
|
||||
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
|
||||
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
|
||||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
|
||||
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
|
||||
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
|
||||
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
|
||||
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
|
||||
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
|
||||
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
|
||||
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
|
||||
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
|
||||
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
|
||||
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
|
||||
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
|
||||
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
|
||||
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
|
||||
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
|
||||
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
|
||||
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
|
||||
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
|
||||
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
|
||||
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
|
||||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
|
||||
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
|
||||
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
|
||||
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
|
||||
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
|
||||
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
|
||||
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
|
||||
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
|
||||
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
|
||||
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
|
||||
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
|
||||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
|
||||
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
|
||||
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
|
||||
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
|
||||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
|
||||
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
|
||||
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
|
||||
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
|
||||
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
||||
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
|
||||
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
|
||||
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
|
||||
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
|
||||
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
|
||||
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
|
@ -1,40 +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/.
|
||||
|
||||
#annotation-list .annotation-details
|
||||
{
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
border: solid 3px #EEE;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#annotation-list .url, .selection-text, .annotation-text
|
||||
{
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#annotation-list .selection-text,#annotation-list .annotation-text
|
||||
{
|
||||
border: solid 1px #EEE;
|
||||
}
|
||||
|
||||
#annotation-list .annotation-text
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
background-color: #F5F5F5;
|
||||
font: 100% arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-family: georgia,serif;
|
||||
font-size: 1.5em;
|
||||
text-align:center;
|
||||
}
|
@ -1,26 +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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Saved annotations</title>
|
||||
<link rel="stylesheet" type="text/css" href="annotation-list.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="annotation-list">
|
||||
</div>
|
||||
|
||||
<div id="template">
|
||||
<div class="annotation-details">
|
||||
<a class="url"></a>
|
||||
<div class="selection-text"></div>
|
||||
<div class="annotation-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,31 +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/. */
|
||||
|
||||
/*
|
||||
Construct the HTML for the annotation list.
|
||||
|
||||
Bind a function to click events on the link that send a message back to
|
||||
the add-on code, so it can open the link in the main browser.
|
||||
*/
|
||||
|
||||
self.on("message", function onMessage(storedAnnotations) {
|
||||
var annotationList = $('#annotation-list');
|
||||
annotationList.empty();
|
||||
storedAnnotations.forEach(
|
||||
function(storedAnnotation) {
|
||||
var annotationHtml = $('#template .annotation-details').clone();
|
||||
annotationHtml.find('.url').text(storedAnnotation.url)
|
||||
.attr('href', storedAnnotation.url);
|
||||
annotationHtml.find('.url').bind('click', function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
self.postMessage(storedAnnotation.url);
|
||||
});
|
||||
annotationHtml.find('.selection-text')
|
||||
.text(storedAnnotation.anchorText);
|
||||
annotationHtml.find('.annotation-text')
|
||||
.text(storedAnnotation.annotationText);
|
||||
annotationList.append(annotationHtml);
|
||||
});
|
||||
});
|
@ -1,50 +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/. */
|
||||
|
||||
/*
|
||||
Locate anchors for annotations and prepare to display the annotations.
|
||||
|
||||
For each annotation, if its URL matches this page,
|
||||
- get the ancestor whose ID matches the ID in the anchor
|
||||
- look for a <p> element whose content contains the anchor text
|
||||
|
||||
That's considered a match. Then we:
|
||||
- highlight the anchor element
|
||||
- add an 'annotated' class to tell the selector to skip this element
|
||||
- embed the annottion text as a new attribute
|
||||
|
||||
For all annotated elements:
|
||||
- bind 'mouseenter' and 'mouseleave' events to the element, to send 'show'
|
||||
and 'hide' messages back to the add-on.
|
||||
*/
|
||||
|
||||
self.on('message', function onMessage(annotations) {
|
||||
annotations.forEach(
|
||||
function(annotation) {
|
||||
if(annotation.url == document.location.toString()) {
|
||||
createAnchor(annotation);
|
||||
}
|
||||
});
|
||||
|
||||
$('.annotated').css('border', 'solid 3px yellow');
|
||||
|
||||
$('.annotated').bind('mouseenter', function(event) {
|
||||
self.port.emit('show', $(this).attr('annotation'));
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$('.annotated').bind('mouseleave', function() {
|
||||
self.port.emit('hide');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function createAnchor(annotation) {
|
||||
annotationAnchorAncestor = $('#' + annotation.ancestorId)[0] || document.body;
|
||||
annotationAnchor = $(annotationAnchorAncestor).parent().find(
|
||||
':contains("' + annotation.anchorText + '")').last();
|
||||
annotationAnchor.addClass('annotated');
|
||||
annotationAnchor.attr('annotation', annotation.annotationText);
|
||||
}
|
@ -1,73 +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/. */
|
||||
|
||||
/*
|
||||
The selector locates elements that are suitable for annotation and enables
|
||||
the user to select them.
|
||||
|
||||
On 'mouseenter' events associated with <p> elements:
|
||||
- if the selector is active and the element is not already annotated
|
||||
- find the nearest ancestor which has an id attribute: this is supposed to
|
||||
make identification of this element more accurate
|
||||
- highlight the element
|
||||
- bind 'click' for the element to send a message back to the add-on, including
|
||||
all the information associated with the anchor.
|
||||
*/
|
||||
|
||||
var matchedElement = null;
|
||||
var originalBgColor = null;
|
||||
var active = false;
|
||||
|
||||
function resetMatchedElement() {
|
||||
if (matchedElement) {
|
||||
matchedElement.css('background-color', originalBgColor);
|
||||
matchedElement.unbind('click.annotator');
|
||||
}
|
||||
}
|
||||
|
||||
self.on('message', function onMessage(activation) {
|
||||
active = activation;
|
||||
if (!active) {
|
||||
resetMatchedElement();
|
||||
}
|
||||
});
|
||||
|
||||
function getInnerText(element) {
|
||||
// jQuery.text() returns content of <script> tags, we need to ignore those
|
||||
var list = [];
|
||||
element.find("*").andSelf().contents()
|
||||
.filter(function () {
|
||||
return this.nodeType == 3 && this.parentNode.tagName != "SCRIPT";
|
||||
})
|
||||
.each(function () {
|
||||
list.push(this.nodeValue);
|
||||
});
|
||||
return list.join("");
|
||||
}
|
||||
|
||||
$('*').mouseenter(function() {
|
||||
if (!active || $(this).hasClass('annotated')) {
|
||||
return;
|
||||
}
|
||||
resetMatchedElement();
|
||||
ancestor = $(this).closest("[id]");
|
||||
matchedElement = $(this).first();
|
||||
originalBgColor = matchedElement.css('background-color');
|
||||
matchedElement.css('background-color', 'yellow');
|
||||
matchedElement.bind('click.annotator', function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
self.port.emit('show',
|
||||
[
|
||||
document.location.toString(),
|
||||
ancestor.attr("id"),
|
||||
getInnerText(matchedElement)
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
$('*').mouseout(function() {
|
||||
resetMatchedElement();
|
||||
});
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.0 KiB |
@ -1,17 +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/. */
|
||||
|
||||
/*
|
||||
Listen for left and right click events and send the corresponding message
|
||||
to the content script.
|
||||
*/
|
||||
|
||||
this.addEventListener('click', function(event) {
|
||||
if(event.button == 0 && event.shiftKey == false)
|
||||
self.port.emit('left-click');
|
||||
|
||||
if(event.button == 2 || (event.button == 0 && event.shiftKey == true))
|
||||
self.port.emit('right-click');
|
||||
event.preventDefault();
|
||||
}, true);
|
@ -1,266 +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/. */
|
||||
|
||||
var widgets = require('sdk/widget');
|
||||
var pageMod = require('sdk/page-mod');
|
||||
var data = require('sdk/self').data;
|
||||
var panels = require('sdk/panel');
|
||||
var simpleStorage = require('sdk/simple-storage');
|
||||
var notifications = require("sdk/notifications");
|
||||
|
||||
/*
|
||||
Global variables
|
||||
* Boolean to indicate whether the add-on is switched on or not
|
||||
* Array for all workers associated with the 'selector' page mod
|
||||
* Array for all workers associated with the 'matcher' page mod
|
||||
*/
|
||||
var annotatorIsOn = false;
|
||||
var selectors = [];
|
||||
var matchers = [];
|
||||
|
||||
if (!simpleStorage.storage.annotations)
|
||||
simpleStorage.storage.annotations = [];
|
||||
|
||||
/*
|
||||
Update the matchers: call this whenever the set of annotations changes
|
||||
*/
|
||||
function updateMatchers() {
|
||||
matchers.forEach(function (matcher) {
|
||||
matcher.postMessage(simpleStorage.storage.annotations);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Constructor for an Annotation object
|
||||
*/
|
||||
function Annotation(annotationText, anchor) {
|
||||
this.annotationText = annotationText;
|
||||
this.url = anchor[0];
|
||||
this.ancestorId = anchor[1];
|
||||
this.anchorText = anchor[2];
|
||||
}
|
||||
|
||||
/*
|
||||
Function to deal with a new annotation.
|
||||
Create a new annotation object, store it, and
|
||||
notify all the annotators of the change.
|
||||
*/
|
||||
function handleNewAnnotation(annotationText, anchor) {
|
||||
var newAnnotation = new Annotation(annotationText, anchor);
|
||||
simpleStorage.storage.annotations.push(newAnnotation);
|
||||
updateMatchers();
|
||||
}
|
||||
|
||||
/*
|
||||
Function to tell the selector page mod that the add-on has become (in)active
|
||||
*/
|
||||
function activateSelectors() {
|
||||
selectors.forEach(
|
||||
function (selector) {
|
||||
selector.postMessage(annotatorIsOn);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Toggle activation: update the on/off state and notify the selectors.
|
||||
*/
|
||||
function toggleActivation() {
|
||||
annotatorIsOn = !annotatorIsOn;
|
||||
activateSelectors();
|
||||
return annotatorIsOn;
|
||||
}
|
||||
|
||||
function detachWorker(worker, workerArray) {
|
||||
var index = workerArray.indexOf(worker);
|
||||
if(index != -1) {
|
||||
workerArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
exports.main = function() {
|
||||
|
||||
/*
|
||||
The widget provides a mechanism to switch the selector on or off, and to
|
||||
view the list of annotations.
|
||||
|
||||
The selector is switched on/off with a left-click, and the list of annotations
|
||||
is displayed on a right-click.
|
||||
*/
|
||||
var widget = widgets.Widget({
|
||||
id: 'toggle-switch',
|
||||
label: 'Annotator',
|
||||
contentURL: data.url('widget/pencil-off.png'),
|
||||
contentScriptWhen: 'ready',
|
||||
contentScriptFile: data.url('widget/widget.js')
|
||||
});
|
||||
|
||||
widget.port.on('left-click', function() {
|
||||
console.log('activate/deactivate');
|
||||
widget.contentURL = toggleActivation() ?
|
||||
data.url('widget/pencil-on.png') :
|
||||
data.url('widget/pencil-off.png');
|
||||
});
|
||||
|
||||
widget.port.on('right-click', function() {
|
||||
console.log('show annotation list');
|
||||
annotationList.show();
|
||||
});
|
||||
|
||||
/*
|
||||
The selector page-mod enables the user to select page elements to annotate.
|
||||
|
||||
It is attached to all pages but only operates if the add-on is active.
|
||||
|
||||
The content script highlights any page elements which can be annotated. If the
|
||||
user clicks a highlighted element it sends a message to the add-on containing
|
||||
information about the element clicked, which is called the anchor of the
|
||||
annotation.
|
||||
|
||||
When we receive this message we assign the anchor to the annotationEditor and
|
||||
display it.
|
||||
*/
|
||||
var selector = pageMod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptWhen: 'ready',
|
||||
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
|
||||
data.url('selector.js')],
|
||||
onAttach: function(worker) {
|
||||
worker.postMessage(annotatorIsOn);
|
||||
selectors.push(worker);
|
||||
worker.port.on('show', function(data) {
|
||||
annotationEditor.annotationAnchor = data;
|
||||
annotationEditor.show();
|
||||
});
|
||||
worker.on('detach', function () {
|
||||
detachWorker(this, selectors);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
The annotationEditor panel is the UI component used for creating
|
||||
new annotations. It contains a text area for the user to
|
||||
enter the annotation.
|
||||
|
||||
When we are ready to display the editor we assign its 'anchor' property
|
||||
and call its show() method.
|
||||
|
||||
Its content script sends the content of the text area to the add-on
|
||||
when the user presses the return key.
|
||||
|
||||
When we receives this message we create a new annotation using the anchor
|
||||
and the text the user entered, store it, and hide the panel.
|
||||
*/
|
||||
var annotationEditor = panels.Panel({
|
||||
width: 220,
|
||||
height: 220,
|
||||
contentURL: data.url('editor/annotation-editor.html'),
|
||||
contentScriptFile: data.url('editor/annotation-editor.js'),
|
||||
onMessage: function(annotationText) {
|
||||
if (annotationText)
|
||||
handleNewAnnotation(annotationText, this.annotationAnchor);
|
||||
annotationEditor.hide();
|
||||
},
|
||||
onShow: function() {
|
||||
this.postMessage('focus');
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
The annotationList panel is the UI component that lists all the annotations
|
||||
the user has entered.
|
||||
|
||||
On its 'show' event we pass it the array of annotations.
|
||||
|
||||
The content script creates the HTML elements for the annotations, and
|
||||
intercepts clicks on the links, passing them back to the add-on to open them
|
||||
in the browser.
|
||||
*/
|
||||
var annotationList = panels.Panel({
|
||||
width: 420,
|
||||
height: 200,
|
||||
contentURL: data.url('list/annotation-list.html'),
|
||||
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
|
||||
data.url('list/annotation-list.js')],
|
||||
contentScriptWhen: 'ready',
|
||||
onShow: function() {
|
||||
this.postMessage(simpleStorage.storage.annotations);
|
||||
},
|
||||
onMessage: function(message) {
|
||||
require('sdk/tabs').open(message);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
We listen for the OverQuota event from simple-storage.
|
||||
If it fires we just notify the user and delete the most
|
||||
recent annotations until we are back in quota.
|
||||
*/
|
||||
simpleStorage.on("OverQuota", function () {
|
||||
notifications.notify({
|
||||
title: 'Storage space exceeded',
|
||||
text: 'Removing recent annotations'});
|
||||
while (simpleStorage.quotaUsage > 1)
|
||||
simpleStorage.storage.annotations.pop();
|
||||
});
|
||||
|
||||
/*
|
||||
The matcher page-mod locates anchors on web pages and prepares for the
|
||||
annotation to be displayed.
|
||||
|
||||
It is attached to all pages, and when it is attached we pass it the complete
|
||||
list of annotations. It looks for anchors in its page. If it finds one it
|
||||
highlights the anchor and binds mouseenter/mouseout events to 'show' and 'hide'
|
||||
messages to the add-on.
|
||||
|
||||
When the add-on receives the 'show' message it assigns the annotation text to
|
||||
the annotation panel and shows it.
|
||||
|
||||
Note that the matcher is active whether or not the add-on is active:
|
||||
'inactive' only means that the user can't create new add-ons, they can still
|
||||
see old ones.
|
||||
*/
|
||||
var matcher = pageMod.PageMod({
|
||||
include: ['*'],
|
||||
contentScriptWhen: 'ready',
|
||||
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
|
||||
data.url('matcher.js')],
|
||||
onAttach: function(worker) {
|
||||
if(simpleStorage.storage.annotations) {
|
||||
worker.postMessage(simpleStorage.storage.annotations);
|
||||
}
|
||||
worker.port.on('show', function(data) {
|
||||
annotation.content = data;
|
||||
annotation.show();
|
||||
});
|
||||
worker.port.on('hide', function() {
|
||||
annotation.content = null;
|
||||
annotation.hide();
|
||||
});
|
||||
worker.on('detach', function () {
|
||||
detachWorker(this, matchers);
|
||||
});
|
||||
matchers.push(worker);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
The annotation panel is the UI component that displays an annotation.
|
||||
|
||||
When we are ready to show it we assign its 'content' attribute to contain
|
||||
the annotation text, and that gets sent to the content process in onShow().
|
||||
*/
|
||||
var annotation = panels.Panel({
|
||||
width: 200,
|
||||
height: 180,
|
||||
contentURL: data.url('annotation/annotation.html'),
|
||||
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
|
||||
data.url('annotation/annotation.js')],
|
||||
contentScriptWhen: 'ready',
|
||||
onShow: function() {
|
||||
this.postMessage(this.content);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"license": "MPL 2.0",
|
||||
"name": "annotator",
|
||||
"contributors": [],
|
||||
"author": "Will Bamberg",
|
||||
"keywords": [],
|
||||
"version": "0.1.1",
|
||||
"id": "anonid0-annotator@jetpack",
|
||||
"description": "Add notes to Web pages",
|
||||
"main": "./lib/main.js"
|
||||
}
|
@ -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/. */
|
||||
"use strict";
|
||||
|
||||
exports.testMain = function(assert) {
|
||||
assert.pass("TODO: Write some tests.");
|
||||
};
|
||||
|
||||
require("sdk/test").run(exports);
|
@ -6,5 +6,5 @@
|
||||
"version": "0.0.1",
|
||||
"author": "Irakli Gozalishvili",
|
||||
"main": "./index.js",
|
||||
"license": "MPL 2.0"
|
||||
"license": "MPL-2.0"
|
||||
}
|
||||
|
@ -1,13 +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/. -->
|
||||
|
||||
This is a port to the SDK of the
|
||||
[Library Detector add-on](https://addons.mozilla.org/en-US/firefox/addon/library-detector/).
|
||||
The original Library Detector is written by
|
||||
[Paul Bakaus](http://paulbakaus.com/) and made available under the
|
||||
[MIT License](http://www.opensource.org/licenses/mit-license.php).
|
||||
|
||||
It only recognizes a subset of the libraries recognized by the original,
|
||||
just to keep the SDK download package size and on-disk footprint as small
|
||||
as possible.
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 386 B |
Before Width: | Height: | Size: 6.4 KiB |
@ -1,97 +0,0 @@
|
||||
/*
|
||||
The code in this file is adapted from the original
|
||||
Library Detector add-on
|
||||
(https://addons.mozilla.org/en-US/firefox/addon/library-detector/) written by
|
||||
Paul Bakaus (http://paulbakaus.com/) and made available under the
|
||||
MIT License (http://www.opensource.org/licenses/mit-license.php).
|
||||
*/
|
||||
|
||||
var LD_tests = {
|
||||
|
||||
'jQuery': {
|
||||
test: function(win) {
|
||||
var jq = win.jQuery || win.$ || win.$jq || win.$j;
|
||||
if(jq && jq.fn && jq.fn.jquery) {
|
||||
return { version: jq.fn.jquery };
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'jQuery UI': {
|
||||
//phonehome: 'http://jqueryui.com/phone_home',
|
||||
test: function(win) {
|
||||
|
||||
var jq = win.jQuery || win.$ || win.$jq || win.$j;
|
||||
if(jq && jq.fn && jq.fn.jquery && jq.ui) {
|
||||
|
||||
var plugins = 'accordion,datepicker,dialog,draggable,droppable,progressbar,resizable,selectable,slider,menu,grid,tabs'.split(','), concat = [];
|
||||
for (var i=0; i < plugins.length; i++) { if(jq.ui[plugins[i]]) concat.push(plugins[i].substr(0,1).toUpperCase() + plugins[i].substr(1)); };
|
||||
|
||||
return { version: jq.ui.version, details: concat.length ? 'Plugins used: '+concat.join(',') : '' };
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
'MooTools': {
|
||||
test: function(win) {
|
||||
if(win.MooTools && win.MooTools.version) {
|
||||
return { version: win.MooTools.version };
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'YUI': {
|
||||
test: function(win) {
|
||||
if(win.YAHOO && win.YAHOO.VERSION) {
|
||||
return { version: win.YAHOO.VERSION };
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'Closure': {
|
||||
test: function(win) {
|
||||
if(win.goog) {
|
||||
return { version: '2.0' };
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
'Modernizr': {
|
||||
test: function(win) {
|
||||
if(win.Modernizr) {
|
||||
return { version: win.Modernizr._version };
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
function testLibraries() {
|
||||
var win = unsafeWindow;
|
||||
var libraryList = [];
|
||||
for(var i in LD_tests) {
|
||||
var passed = LD_tests[i].test(win);
|
||||
if (passed) {
|
||||
let libraryInfo = {
|
||||
name: i,
|
||||
version: passed.version
|
||||
};
|
||||
libraryList.push(libraryInfo);
|
||||
}
|
||||
}
|
||||
self.postMessage(libraryList);
|
||||
}
|
||||
|
||||
testLibraries();
|
@ -1,16 +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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Library detector</title>
|
||||
<script type="text/javascript">
|
||||
addon.on('message', function (libraryInfo) {
|
||||
document.body.innerHTML = libraryInfo;
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
@ -1,50 +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/. -->
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
<title>Library detector</title>
|
||||
<style type="text/css" media="all">
|
||||
img {
|
||||
display: inline;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var icons = {
|
||||
'jQuery' : 'jquery.ico',
|
||||
'jQuery UI' : 'jquery_ui.ico',
|
||||
'MooTools' : 'mootools.png',
|
||||
'YUI' : 'yui.ico',
|
||||
'Closure' : 'closure.ico',
|
||||
'Modernizr': 'modernizr.ico',
|
||||
};
|
||||
|
||||
// Listen for mouse events over icons, in order to send a message up to
|
||||
// the panel and update its content with library name and version
|
||||
window.addEventListener('mouseover', function (event) {
|
||||
if (event.target.tagName == 'IMG') {
|
||||
addon.port.emit('setLibraryInfo', event.target.title);
|
||||
}
|
||||
}, false);
|
||||
|
||||
addon.port.on('update', function (libraries) {
|
||||
// Cleanup previous content
|
||||
document.body.innerHTML = '';
|
||||
|
||||
// Create new updated list of icons
|
||||
libraries.forEach(function(library) {
|
||||
var img = document.createElement('img');
|
||||
img.setAttribute('src', 'icons/' + icons[library.name]);
|
||||
img.setAttribute('title', library.name + "<br>Version: " +
|
||||
library.version);
|
||||
document.body.appendChild(img);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
@ -1,67 +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/. */
|
||||
|
||||
const tabs = require('sdk/tabs');
|
||||
const widgets = require('sdk/widget');
|
||||
const data = require('sdk/self').data;
|
||||
const pageMod = require('sdk/page-mod');
|
||||
const panel = require('sdk/panel');
|
||||
|
||||
const ICON_WIDTH = 16;
|
||||
|
||||
function updateWidgetView(tab) {
|
||||
let widgetView = widget.getView(tab.window);
|
||||
if (!tab.libraries) {
|
||||
tab.libraries = [];
|
||||
}
|
||||
widgetView.port.emit("update", tab.libraries);
|
||||
widgetView.width = tab.libraries.length * ICON_WIDTH;
|
||||
}
|
||||
|
||||
var widget = widgets.Widget({
|
||||
id: "library-detector",
|
||||
label: "Library Detector",
|
||||
contentURL: data.url("widget.html"),
|
||||
panel: panel.Panel({
|
||||
width: 240,
|
||||
height: 60,
|
||||
contentURL: data.url("panel.html")
|
||||
})
|
||||
});
|
||||
|
||||
widget.port.on('setLibraryInfo', function(libraryInfo) {
|
||||
widget.panel.postMessage(libraryInfo);
|
||||
});
|
||||
|
||||
pageMod.PageMod({
|
||||
include: "*",
|
||||
contentScriptWhen: 'end',
|
||||
contentScriptFile: (data.url('library-detector.js')),
|
||||
onAttach: function(worker) {
|
||||
worker.on('message', function(libraryList) {
|
||||
if (!worker.tab.libraries) {
|
||||
worker.tab.libraries = [];
|
||||
}
|
||||
libraryList.forEach(function(library) {
|
||||
if (worker.tab.libraries.indexOf(library) == -1) {
|
||||
worker.tab.libraries.push(library);
|
||||
}
|
||||
});
|
||||
if (worker.tab == tabs.activeTab) {
|
||||
updateWidgetView(worker.tab);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tabs.on('activate', function(tab) {
|
||||
updateWidgetView(tab);
|
||||
});
|
||||
|
||||
/*
|
||||
For change of location
|
||||
*/
|
||||
tabs.on('ready', function(tab) {
|
||||
tab.libraries = [];
|
||||
});
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "library-detector-sdk",
|
||||
"license": "MPL 2.0",
|
||||
"author": "",
|
||||
"version": "0.1.1",
|
||||
"title": "library-detector-sdk",
|
||||
"id": "jid1-R4rSVNkBANnvGQ@jetpack",
|
||||
"description": "a basic add-on",
|
||||
"main": "./lib/main.js"
|
||||
}
|
@ -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/. */
|
||||
"use strict";
|
||||
|
||||
exports.testMain = function(assert) {
|
||||
assert.pass("TODO: Write some tests.");
|
||||
};
|
||||
|
||||
require("sdk/test").run(exports);
|
@ -4,6 +4,6 @@
|
||||
"keywords": [],
|
||||
"author": "Brian Warner",
|
||||
"contributors": [],
|
||||
"license": "MPL 2.0",
|
||||
"license": "MPL-2.0",
|
||||
"id": "reading-data-example@jetpack.mozillalabs.com"
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"id": "theme@jetpack",
|
||||
"description": "How to create new theme for devtools",
|
||||
"author": "Jan Odvarko",
|
||||
"license": "MPL 2.0",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.0",
|
||||
"main": "lib/main"
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"main": "./lib/main.js",
|
||||
"description": "a toolbar api example",
|
||||
"author": "",
|
||||
"license": "MPL 2.0",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.1",
|
||||
"engines": {
|
||||
"firefox": ">=27.0 <=30.0"
|
||||
|
@ -4,7 +4,7 @@
|
||||
"id": "ui-button-apis@mozilla.org",
|
||||
"description": "A Button API example",
|
||||
"author": "jeff@canuckistani.ca (Jeff Griffiths | @canuckistani)",
|
||||
"license": "MPL 2.0",
|
||||
"license": "MPL-2.0",
|
||||
"version": "0.1.1",
|
||||
"main": "./lib/main.js"
|
||||
}
|
||||
|
@ -4,20 +4,41 @@
|
||||
"use strict";
|
||||
|
||||
var gulp = require('gulp');
|
||||
var patch = require("./bin/node-scripts/apply-patch");
|
||||
var ini = require("./bin/node-scripts/update-ini");
|
||||
|
||||
gulp.task('test', function(done) {
|
||||
require("./bin/jpm-test").run().then(done);
|
||||
});
|
||||
|
||||
gulp.task('test:addons', function(done) {
|
||||
require("./bin/jpm-test").run("addons").then(done);
|
||||
require("./bin/jpm-test").run("addons").catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('test:docs', function(done) {
|
||||
require("./bin/jpm-test").run("docs").catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('test:examples', function(done) {
|
||||
require("./bin/jpm-test").run("examples").then(done);
|
||||
require("./bin/jpm-test").run("examples").catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('test:modules', function(done) {
|
||||
require("./bin/jpm-test").run("modules").then(done);
|
||||
require("./bin/jpm-test").run("modules").catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('test:ini', function(done) {
|
||||
test("ini").catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('patch:clean', function(done) {
|
||||
patch.clean().catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('patch:apply', function(done) {
|
||||
patch.apply().catch(console.error).then(done);
|
||||
});
|
||||
|
||||
gulp.task('update:ini', function(done) {
|
||||
ini.updateAddonINI().catch(console.error).then(done);
|
||||
});
|
||||
|
@ -72,6 +72,9 @@ addMessageListener("sdk/event/message", onInMessage);
|
||||
addMessageListener("sdk/port/message", onInPort);
|
||||
|
||||
const observer = {
|
||||
handleEvent: ({target, type}) => {
|
||||
observer.observe(target, type);
|
||||
},
|
||||
observe: (document, topic, data) => {
|
||||
// When frame associated with message manager is removed from document `docShell`
|
||||
// is set to `null` but observer is still kept alive. At this point accesing
|
||||
@ -82,21 +85,21 @@ const observer = {
|
||||
observerService.removeObserver(observer, topic);
|
||||
}
|
||||
else if (document === content.document) {
|
||||
if (topic === "content-document-interactive") {
|
||||
if (topic.endsWith("-document-interactive")) {
|
||||
sendAsyncMessage("sdk/event/ready", {
|
||||
type: "ready",
|
||||
readyState: document.readyState,
|
||||
uri: document.documentURI
|
||||
});
|
||||
}
|
||||
if (topic === "content-document-loaded") {
|
||||
if (topic.endsWith("-document-loaded")) {
|
||||
sendAsyncMessage("sdk/event/load", {
|
||||
type: "load",
|
||||
readyState: document.readyState,
|
||||
uri: document.documentURI
|
||||
});
|
||||
}
|
||||
if (topic === "content-page-hidden") {
|
||||
if (topic === "unload") {
|
||||
channels.clear();
|
||||
sendAsyncMessage("sdk/event/unload", {
|
||||
type: "unload",
|
||||
@ -110,6 +113,8 @@ const observer = {
|
||||
|
||||
observerService.addObserver(observer, "content-document-interactive", false);
|
||||
observerService.addObserver(observer, "content-document-loaded", false);
|
||||
observerService.addObserver(observer, "content-page-hidden", false);
|
||||
observerService.addObserver(observer, "chrome-document-interactive", false);
|
||||
observerService.addObserver(observer, "chrome-document-loaded", false);
|
||||
addEventListener("unload", observer, false);
|
||||
|
||||
})(this);
|
||||
|
@ -126,7 +126,7 @@ const Panel = Class({
|
||||
return isUninitialized ? Promise.resolve(this) :
|
||||
when(this, "unload").then(getTarget);
|
||||
},
|
||||
postMessage: function(data, ports) {
|
||||
postMessage: function(data, ports=[]) {
|
||||
const manager = managerFor(this);
|
||||
manager.sendAsyncMessage("sdk/event/message", {
|
||||
type: "message",
|
||||
@ -227,11 +227,11 @@ createView.define(Panel, (panel, document) => {
|
||||
const frame = document.createElement("iframe");
|
||||
setAttributes(frame, {
|
||||
"sandbox": "allow-scripts",
|
||||
// It would be great if we could allow remote iframes for sandboxing
|
||||
// panel documents in a content process, but for now platform implementation
|
||||
// is buggy on linux so this is disabled.
|
||||
// "remote": true,
|
||||
"type": "content",
|
||||
// We end up using chrome iframe with forced message manager
|
||||
// as fixing a swapFrameLoader seemed like a giant task (see
|
||||
// Bug 1075490).
|
||||
"type": "chrome",
|
||||
"forcemessagemanager": true,
|
||||
"transparent": true,
|
||||
"seamless": "seamless",
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* 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/. */
|
||||
|
||||
!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.volcan=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
|
||||
"use strict";
|
||||
|
||||
|
53
addon-sdk/source/lib/jetpack-id/index.js
Normal file
@ -0,0 +1,53 @@
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Takes parsed `package.json` manifest and returns
|
||||
* valid add-on id for it.
|
||||
*/
|
||||
function getID(manifest) {
|
||||
manifest = manifest || {};
|
||||
|
||||
if (manifest.id) {
|
||||
|
||||
if (typeof manifest.id !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If manifest.id is already valid (as domain or GUID), use it
|
||||
if (isValidAOMName(manifest.id)) {
|
||||
return manifest.id;
|
||||
}
|
||||
// Otherwise, this ID is invalid so return `null`
|
||||
return null;
|
||||
}
|
||||
|
||||
// If no `id` defined, turn `name` into a domain ID,
|
||||
// as we transition to `name` being an id, similar to node/npm, but
|
||||
// append a '@' to make it compatible with Firefox requirements
|
||||
if (manifest.name) {
|
||||
|
||||
if (typeof manifest.name !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
var modifiedName = "@" + manifest.name;
|
||||
return isValidAOMName(modifiedName) ? modifiedName : null;
|
||||
}
|
||||
|
||||
// If no `id` or `name` property, return null as this manifest
|
||||
// is invalid
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getID;
|
||||
|
||||
/**
|
||||
* Regex taken from XPIProvider.jsm in the Addon Manager to validate proper
|
||||
* IDs that are able to be used.
|
||||
* http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/internal/XPIProvider.jsm#209
|
||||
*/
|
||||
function isValidAOMName (s) {
|
||||
return /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i.test(s || "");
|
||||
}
|
28
addon-sdk/source/lib/jetpack-id/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "jetpack-id",
|
||||
"version": "1.0.0",
|
||||
"description": "Creates an ID from a Firefox Jetpack manifest",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/jsantell/jetpack-id"
|
||||
},
|
||||
"author": {
|
||||
"name": "Jordan Santell",
|
||||
"url": "http://github.com/jsantell"
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/mocha --reporter spec --ui bdd"
|
||||
},
|
||||
"keywords": [
|
||||
"jetpack",
|
||||
"addon",
|
||||
"mozilla",
|
||||
"firefox"
|
||||
],
|
||||
"devDependencies": {
|
||||
"mocha": "*",
|
||||
"chai": "*"
|
||||
}
|
||||
}
|
112
addon-sdk/source/lib/mozilla-toolkit-versioning/index.js
Normal file
@ -0,0 +1,112 @@
|
||||
/* 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/. */
|
||||
|
||||
var versionParse = require('./lib/utils').versionParse;
|
||||
|
||||
var COMPARATORS = ['>=', '<=', '>', '<', '=', '~', '^'];
|
||||
|
||||
exports.parse = function (input) {
|
||||
input = input || '';
|
||||
input = input.trim();
|
||||
if (!input)
|
||||
throw new Error('`parse` argument must be a populated string.');
|
||||
|
||||
// Handle the "*" case
|
||||
if (input === "*") {
|
||||
return { min: undefined, max: undefined };
|
||||
}
|
||||
|
||||
var inputs = input.split(' ');
|
||||
var min;
|
||||
var max;
|
||||
|
||||
// 1.2.3 - 2.3.4
|
||||
if (inputs.length === 3 && inputs[1] === '-') {
|
||||
return { min: inputs[0], max: inputs[2] };
|
||||
}
|
||||
|
||||
inputs.forEach(function (input) {
|
||||
var parsed = parseExpression(input);
|
||||
var version = parsed.version;
|
||||
var comparator = parsed.comparator;
|
||||
|
||||
// 1.2.3
|
||||
if (inputs.length === 1 && !comparator)
|
||||
min = max = version;
|
||||
|
||||
// Parse min
|
||||
if (~comparator.indexOf('>')) {
|
||||
if (~comparator.indexOf('='))
|
||||
min = version; // >=1.2.3
|
||||
else
|
||||
min = increment(version); // >1.2.3
|
||||
}
|
||||
else if (~comparator.indexOf('<')) {
|
||||
if (~comparator.indexOf('='))
|
||||
max = version; // <=1.2.3
|
||||
else
|
||||
max = decrement(version); // <1.2.3
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
min: min,
|
||||
max : max
|
||||
};
|
||||
};
|
||||
|
||||
function parseExpression (input) {
|
||||
for (var i = 0; i < COMPARATORS.length; i++)
|
||||
if (~input.indexOf(COMPARATORS[i]))
|
||||
return {
|
||||
comparator: COMPARATORS[i],
|
||||
version: input.substr(COMPARATORS[i].length)
|
||||
};
|
||||
return { version: input, comparator: '' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a version string ('1.2.3') and returns a version string
|
||||
* that'll parse as one less than the input string ('1.2.3.-1').
|
||||
*
|
||||
* @param {String} vString
|
||||
* @return {String}
|
||||
*/
|
||||
function decrement (vString) {
|
||||
return vString + (vString.charAt(vString.length - 1) === '.' ? '' : '.') + '-1';
|
||||
}
|
||||
exports.decrement = decrement;
|
||||
|
||||
/**
|
||||
* Takes a version string ('1.2.3') and returns a version string
|
||||
* that'll parse as greater than the input string by the smallest margin
|
||||
* possible ('1.2.3.1').
|
||||
* listed as number-A, string-B, number-C, string-D in
|
||||
* Mozilla's Toolkit Format.
|
||||
* https://developer.mozilla.org/en-US/docs/Toolkit_version_format
|
||||
*
|
||||
* @param {String} vString
|
||||
* @return {String}
|
||||
*/
|
||||
function increment (vString) {
|
||||
var match = versionParse(vString);
|
||||
var a = match[1];
|
||||
var b = match[2];
|
||||
var c = match[3];
|
||||
var d = match[4];
|
||||
var lastPos = vString.length - 1;
|
||||
var lastChar = vString.charAt(lastPos);
|
||||
|
||||
if (!b) {
|
||||
return vString + (lastChar === '.' ? '' : '.') + '1';
|
||||
}
|
||||
if (!c) {
|
||||
return vString + '1';
|
||||
}
|
||||
if (!d) {
|
||||
return vString.substr(0, lastPos) + (++lastChar);
|
||||
}
|
||||
return vString.substr(0, lastPos) + String.fromCharCode(lastChar.charCodeAt(0) + 1);
|
||||
}
|
||||
exports.increment = increment;
|
15
addon-sdk/source/lib/mozilla-toolkit-versioning/lib/utils.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Breaks up a version string into the 4 components
|
||||
* defined in:
|
||||
* https://developer.mozilla.org/en-US/docs/Toolkit_version_format
|
||||
* @params {String} val
|
||||
* @return {String}
|
||||
*/
|
||||
function versionParse (val) {
|
||||
return val.match(/^([0-9\.]*)([a-zA-Z]*)([0-9\.]*)([a-zA-Z]*)$/);
|
||||
}
|
||||
exports.versionParse = versionParse;
|
21
addon-sdk/source/lib/mozilla-toolkit-versioning/package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "mozilla-toolkit-versioning",
|
||||
"version": "0.0.2",
|
||||
"description": "Parser for Mozilla's toolkit version format",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/mocha --reporter spec --ui bdd"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/jsantell/mozilla-toolkit-versioning.git"
|
||||
},
|
||||
"author": "Jordan Santell",
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"mocha": "^1.21.4",
|
||||
"chai": "^1.9.1",
|
||||
"mozilla-version-comparator": "^1.0.2"
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@ module.metadata = {
|
||||
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const { DataURL } = require("./url");
|
||||
const errors = require("./deprecated/errors");
|
||||
const apiUtils = require("./deprecated/api-utils");
|
||||
/*
|
||||
While these data flavors resemble Internet media types, they do
|
||||
|
@ -110,7 +110,7 @@ Object.freeze({
|
||||
injectTimers: function injectTimers(exports, chromeAPI, pipe, console) {
|
||||
// wrapped functions from `'timer'` module.
|
||||
// Wrapper adds `try catch` blocks to the callbacks in order to
|
||||
// emit `error` event on a symbiont if exception is thrown in
|
||||
// emit `error` event if exception is thrown in
|
||||
// the Worker global scope.
|
||||
// @see http://www.w3.org/TR/workers/#workerutils
|
||||
|
||||
|
@ -9,21 +9,6 @@ module.metadata = {
|
||||
|
||||
const { deprecateUsage } = require('../util/deprecate');
|
||||
|
||||
Object.defineProperty(exports, "Loader", {
|
||||
get: function() {
|
||||
deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/loader` directly.');
|
||||
return require('./loader').Loader;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(exports, "Symbiont", {
|
||||
get: function() {
|
||||
deprecateUsage('Both `sdk/content/content` and `sdk/deprecated/symbiont` are deprecated. ' +
|
||||
'`sdk/core/heritage` supersedes Symbiont for inheritance.');
|
||||
return require('../deprecated/symbiont').Symbiont;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(exports, "Worker", {
|
||||
get: function() {
|
||||
deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/worker` directly.');
|
||||
|
@ -1,16 +1,13 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { EventEmitter } = require('../deprecated/events');
|
||||
const { isValidURI, isLocalURL, URL } = require('../url');
|
||||
const file = require('../io/file');
|
||||
const { contract } = require('../util/contract');
|
||||
const { isString, isNil, instanceOf } = require('../lang/type');
|
||||
const { validateOptions,
|
||||
@ -19,7 +16,8 @@ const { validateOptions,
|
||||
const isJSONable = (value) => {
|
||||
try {
|
||||
JSON.parse(JSON.stringify(value));
|
||||
} catch (e) {
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -75,102 +73,4 @@ function Allow(script) ({
|
||||
set script(value) script = !!value
|
||||
})
|
||||
|
||||
/**
|
||||
* Trait is intended to be used in some composition. It provides set of core
|
||||
* properties and bounded validations to them. Trait is useful for all the
|
||||
* compositions providing high level APIs for interaction with content.
|
||||
* Property changes emit `"propertyChange"` events on instances.
|
||||
*/
|
||||
const Loader = EventEmitter.compose({
|
||||
/**
|
||||
* Permissions for the content, with the following keys:
|
||||
* @property {Object} [allow = { script: true }]
|
||||
* @property {Boolean} [allow.script = true]
|
||||
* Whether or not to execute script in the content. Defaults to true.
|
||||
*/
|
||||
get allow() this._allow || (this._allow = Allow(true)),
|
||||
set allow(value) this.allow.script = value && value.script,
|
||||
_allow: null,
|
||||
/**
|
||||
* The content to load. Either a string of HTML or a URL.
|
||||
* @type {String}
|
||||
*/
|
||||
get contentURL() this._contentURL,
|
||||
set contentURL(value) {
|
||||
value = validate(value, valid.contentURL);
|
||||
if (this._contentURL != value) {
|
||||
this._emit('propertyChange', {
|
||||
contentURL: this._contentURL = value
|
||||
});
|
||||
}
|
||||
},
|
||||
_contentURL: null,
|
||||
/**
|
||||
* When to load the content scripts.
|
||||
* Possible values are "end" (default), which loads them once all page
|
||||
* contents have been loaded, "ready", which loads them once DOM nodes are
|
||||
* ready (ie like DOMContentLoaded event), and "start", which loads them once
|
||||
* the `window` object for the page has been created, but before any scripts
|
||||
* specified by the page have been loaded.
|
||||
* Property change emits `propertyChange` event on instance with this key
|
||||
* and new value.
|
||||
* @type {'start'|'ready'|'end'}
|
||||
*/
|
||||
get contentScriptWhen() this._contentScriptWhen,
|
||||
set contentScriptWhen(value) {
|
||||
value = validate(value, valid.contentScriptWhen);
|
||||
if (value !== this._contentScriptWhen) {
|
||||
this._emit('propertyChange', {
|
||||
contentScriptWhen: this._contentScriptWhen = value
|
||||
});
|
||||
}
|
||||
},
|
||||
_contentScriptWhen: 'end',
|
||||
/**
|
||||
* Options avalaible from the content script as `self.options`.
|
||||
* The value of options can be of any type (object, array, string, etc.)
|
||||
* but only jsonable values will be available as frozen objects from the
|
||||
* content script.
|
||||
* Property change emits `propertyChange` event on instance with this key
|
||||
* and new value.
|
||||
* @type {Object}
|
||||
*/
|
||||
get contentScriptOptions() this._contentScriptOptions,
|
||||
set contentScriptOptions(value) this._contentScriptOptions = value,
|
||||
_contentScriptOptions: null,
|
||||
/**
|
||||
* The URLs of content scripts.
|
||||
* Property change emits `propertyChange` event on instance with this key
|
||||
* and new value.
|
||||
* @type {String[]}
|
||||
*/
|
||||
get contentScriptFile() this._contentScriptFile,
|
||||
set contentScriptFile(value) {
|
||||
value = validate(value, valid.contentScriptFile);
|
||||
if (value != this._contentScriptFile) {
|
||||
this._emit('propertyChange', {
|
||||
contentScriptFile: this._contentScriptFile = value
|
||||
});
|
||||
}
|
||||
},
|
||||
_contentScriptFile: null,
|
||||
/**
|
||||
* The texts of content script.
|
||||
* Property change emits `propertyChange` event on instance with this key
|
||||
* and new value.
|
||||
* @type {String|undefined}
|
||||
*/
|
||||
get contentScript() this._contentScript,
|
||||
set contentScript(value) {
|
||||
value = validate(value, valid.contentScript);
|
||||
if (value != this._contentScript) {
|
||||
this._emit('propertyChange', {
|
||||
contentScript: this._contentScript = value
|
||||
});
|
||||
}
|
||||
},
|
||||
_contentScript: null
|
||||
});
|
||||
exports.Loader = Loader;
|
||||
|
||||
exports.contract = contract(valid);
|
||||
|
@ -116,7 +116,7 @@ const WorkerSandbox = Class({
|
||||
// (This behavior can be turned off for now with the unsafe-content-script
|
||||
// flag to give addon developers time for making the necessary changes)
|
||||
// But prevent it when the Worker isn't used for a content script but for
|
||||
// injecting `addon` object into a Panel, Widget, ... scope.
|
||||
// injecting `addon` object into a Panel scope, for example.
|
||||
// That's because:
|
||||
// 1/ It is useless to use multiple domains as the worker is only used
|
||||
// to communicate with the addon,
|
||||
@ -214,7 +214,7 @@ const WorkerSandbox = Class({
|
||||
}
|
||||
|
||||
// Inject our `console` into target document if worker doesn't have a tab
|
||||
// (e.g Panel, PageWorker, Widget).
|
||||
// (e.g Panel, PageWorker).
|
||||
// `worker.tab` can't be used because bug 804935.
|
||||
if (!isWindowInTab(window)) {
|
||||
let win = getUnsafeWindow(window);
|
||||
|
@ -34,6 +34,8 @@ const WorkerChild = Class({
|
||||
keepAlive.set(this.id, this);
|
||||
|
||||
this.windowId = getInnerId(this.window);
|
||||
if (this.contentScriptOptions)
|
||||
this.contentScriptOptions = JSON.parse(this.contentScriptOptions);
|
||||
|
||||
this.port = EventTarget();
|
||||
this.port.on('*', this.send.bind(this, 'event'));
|
||||
@ -53,7 +55,7 @@ const WorkerChild = Class({
|
||||
this.frozenMessages = [];
|
||||
this.on('pageshow', () => {
|
||||
this.frozen = false;
|
||||
this.frozenMessages.forEach(args => this.receive(null, this.id, args));
|
||||
this.frozenMessages.forEach(args => this.sandbox.emit(...args));
|
||||
this.frozenMessages = [];
|
||||
});
|
||||
this.on('pagehide', () => {
|
||||
@ -65,6 +67,7 @@ const WorkerChild = Class({
|
||||
receive(process, id, args) {
|
||||
if (id !== this.id)
|
||||
return;
|
||||
args = JSON.parse(args);
|
||||
|
||||
if (this.frozen)
|
||||
this.frozenMessages.push(args);
|
||||
@ -76,8 +79,7 @@ const WorkerChild = Class({
|
||||
},
|
||||
|
||||
send(...args) {
|
||||
args = JSON.parse(JSON.stringify(args, exceptions));
|
||||
process.port.emit('sdk/worker/event', this.id, args);
|
||||
process.port.emit('sdk/worker/event', this.id, JSON.stringify(args, exceptions));
|
||||
},
|
||||
|
||||
// notifications
|
||||
@ -131,7 +133,7 @@ function exceptions(key, value) {
|
||||
let keepAlive = new Map();
|
||||
|
||||
process.port.on('sdk/worker/create', (process, options) => {
|
||||
options.window = getByInnerId(options.window);
|
||||
options.window = getByInnerId(options.windowId);
|
||||
if (!options.window)
|
||||
return;
|
||||
|
||||
|
@ -67,6 +67,7 @@ const Worker = Class({
|
||||
let model = modelFor(this);
|
||||
if (id !== model.id || !model.attached)
|
||||
return;
|
||||
args = JSON.parse(args);
|
||||
if (model.destroyed && args[0] != 'detach')
|
||||
return;
|
||||
|
||||
@ -86,7 +87,7 @@ const Worker = Class({
|
||||
return;
|
||||
}
|
||||
|
||||
processes.port.emit('sdk/worker/message', model.id, args);
|
||||
processes.port.emit('sdk/worker/message', model.id, JSON.stringify(args));
|
||||
},
|
||||
|
||||
// properties
|
||||
@ -127,15 +128,26 @@ attach.define(Worker, function(worker, window) {
|
||||
if (tab)
|
||||
frame = frames.getFrameForBrowser(getBrowserForTab(tab));
|
||||
|
||||
merge(model.options, {
|
||||
id: String(uuid()),
|
||||
window: getInnerId(window),
|
||||
url: String(window.location)
|
||||
});
|
||||
function makeStringArray(arrayOrValue) {
|
||||
if (!arrayOrValue)
|
||||
return [];
|
||||
return [String(v) for (v of [].concat(arrayOrValue))];
|
||||
}
|
||||
|
||||
processes.port.emit('sdk/worker/create', model.options);
|
||||
let id = String(uuid());
|
||||
let childOptions = {
|
||||
id,
|
||||
windowId: getInnerId(window),
|
||||
contentScript: makeStringArray(model.options.contentScript),
|
||||
contentScriptFile: makeStringArray(model.options.contentScriptFile),
|
||||
contentScriptOptions: model.options.contentScriptOptions ?
|
||||
JSON.stringify(model.options.contentScriptOptions) :
|
||||
null,
|
||||
}
|
||||
|
||||
connect(worker, frame, model.options);
|
||||
processes.port.emit('sdk/worker/create', childOptions);
|
||||
|
||||
connect(worker, frame, { id, url: String(window.location) });
|
||||
})
|
||||
|
||||
connect.define(Worker, function(worker, frame, { id, url }) {
|
||||
|
@ -449,7 +449,7 @@ function workerMessageReceived(process, id, args) {
|
||||
if (internal(this).id != id)
|
||||
return;
|
||||
|
||||
emit(this, ...args);
|
||||
emit(this, ...JSON.parse(args));
|
||||
}
|
||||
|
||||
// All things that have a label on the context menu extend this
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
|
@ -1,14 +1,12 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const memory = require("./memory");
|
||||
|
||||
const { merge } = require("../util/object");
|
||||
const { union } = require("../util/array");
|
||||
const { isNil, isRegExp } = require("../lang/type");
|
||||
|
@ -1,115 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const getOwnIdentifiers = x => [...Object.getOwnPropertyNames(x),
|
||||
...Object.getOwnPropertySymbols(x)];
|
||||
|
||||
|
||||
// `var` is being used in the module in order to make it reusable in
|
||||
// environments in which `let` and `const` is not yet supported.
|
||||
|
||||
// Returns `object`'s property value, where `name` is a name of the property.
|
||||
function get(object, name) {
|
||||
return object[name];
|
||||
}
|
||||
|
||||
// Assigns `value` to the `object`'s property, where `name` is the name of the
|
||||
// property.
|
||||
function set(object, name, value) {
|
||||
return object[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an `object` containing a property with the given `name`, create
|
||||
* a property descriptor that can be used to define alias/proxy properties
|
||||
* on other objects. A change in the value of an alias will propagate
|
||||
* to the aliased property and vice versa.
|
||||
*/
|
||||
function createAliasProperty(object, name) {
|
||||
// Getting own property descriptor of an `object` for the given `name` as
|
||||
// we are going to create proxy analog.
|
||||
var property = Object.getOwnPropertyDescriptor(object, name);
|
||||
var descriptor = {
|
||||
configurable: property.configurable,
|
||||
enumerable: property.enumerable,
|
||||
alias: true
|
||||
};
|
||||
|
||||
// If the original property has a getter and/or setter, bind a
|
||||
// corresponding getter/setter in the alias descriptor to the original
|
||||
// object, so the `this` object in the getter/setter is the original object
|
||||
// rather than the alias.
|
||||
if ("get" in property && property.get)
|
||||
descriptor.get = property.get.bind(object);
|
||||
if ("set" in property && property.set)
|
||||
descriptor.set = property.set.bind(object);
|
||||
|
||||
// If original property was a value property.
|
||||
if ("value" in property) {
|
||||
// If original property is a method using it's `object` bounded copy.
|
||||
if (typeof property.value === "function") {
|
||||
descriptor.value = property.value.bind(object);
|
||||
// Also preserving writability of the original property.
|
||||
descriptor.writable = property.writable;
|
||||
}
|
||||
|
||||
// If the original property was just a data property, we create proxy
|
||||
// accessors using our custom get/set functions to propagate changes to the
|
||||
// original `object` and vice versa.
|
||||
else {
|
||||
descriptor.get = get.bind(null, object, name);
|
||||
descriptor.set = set.bind(null, object, name);
|
||||
}
|
||||
}
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
// Defines property on `object` object with a name `alias` if given if not
|
||||
// defaults to `name` that represents an alias of `source[name]`. If aliased
|
||||
// property was an assessor or a method `this` pseudo-variable will be `source`
|
||||
// when invoked. If aliased property was a data property changes on any of the
|
||||
// aliases will propagate to the `source[name]` and also other way round.
|
||||
function defineAlias(source, target, name, alias) {
|
||||
return Object.defineProperty(target, alias || name,
|
||||
createAliasProperty(source, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function takes any `object` and returns a proxy for its own public
|
||||
* properties. By default properties are considered to be public if they don't
|
||||
* start with `"_"`, but default behavior can be overridden if needed, by
|
||||
* passing array of public property `names` as a second argument. By default
|
||||
* returned object will be direct decedent of the given `object`'s prototype,
|
||||
* but this can be overridden by passing third optional argument, that will be
|
||||
* used as `prototype` instead.
|
||||
* @param {Object} object
|
||||
* Object to create cortex for.
|
||||
* @param {String[]} [names]
|
||||
* Optional array of public property names.
|
||||
* @param {Object} [prototype]
|
||||
* Optional argument that will be used as `prototype` of the returned object,
|
||||
* if not provided `Object.getPrototypeOf(object)` is used instead.
|
||||
*/
|
||||
exports.Cortex = function Cortex(object, names, prototype) {
|
||||
// Creating a cortex object from the given `prototype`, if one was not
|
||||
// provided then `prototype` of a given `object` is used. This allows
|
||||
// consumer to define expected behavior `instanceof`. In common case
|
||||
// `prototype` argument can be omitted to preserve same behavior of
|
||||
// `instanceof` as on original `object`.
|
||||
var cortex = Object.create(prototype || Object.getPrototypeOf(object));
|
||||
// Creating alias properties on the `cortex` object for all the own
|
||||
// properties of the original `object` that are contained in `names` array.
|
||||
// If `names` array is not provided then all the properties that don't
|
||||
// start with `"_"` are aliased.
|
||||
getOwnIdentifiers(object).forEach(function (name) {
|
||||
if ((!names && "_" !== name.toString().charAt(0)) || (names && ~names.indexOf(name)))
|
||||
defineAlias(object, cortex, name);
|
||||
});
|
||||
return cortex;
|
||||
}
|
@ -1,64 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
function logToConsole(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
|
||||
var catchAndLog = exports.catchAndLog = function(callback,
|
||||
defaultResponse,
|
||||
logException) {
|
||||
if (!logException)
|
||||
logException = logToConsole;
|
||||
|
||||
return function() {
|
||||
try {
|
||||
return callback.apply(this, arguments);
|
||||
} catch (e) {
|
||||
logException(e);
|
||||
return defaultResponse;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
exports.catchAndLogProps = function catchAndLogProps(object,
|
||||
props,
|
||||
defaultResponse,
|
||||
logException) {
|
||||
if (typeof(props) == "string")
|
||||
props = [props];
|
||||
props.forEach(
|
||||
function(property) {
|
||||
object[property] = catchAndLog(object[property],
|
||||
defaultResponse,
|
||||
logException);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch and return an exception while calling the callback. If the callback
|
||||
* doesn't throw, return the return value of the callback in a way that makes it
|
||||
* possible to distinguish between a return value and an exception.
|
||||
*
|
||||
* This function is useful when you need to pass the result of a call across
|
||||
* a process boundary (across which exceptions don't propagate). It probably
|
||||
* doesn't need to be factored out into this module, since it is only used by
|
||||
* a single caller, but putting it here works around bug 625560.
|
||||
*/
|
||||
exports.catchAndReturn = function(callback) {
|
||||
return function() {
|
||||
try {
|
||||
return { returnValue: callback.apply(this, arguments) };
|
||||
}
|
||||
catch (exception) {
|
||||
return { exception: exception };
|
||||
}
|
||||
};
|
||||
};
|
@ -1,181 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const ERROR_TYPE = 'error',
|
||||
UNCAUGHT_ERROR = 'An error event was dispatched for which there was'
|
||||
+ ' no listener.',
|
||||
BAD_LISTENER = 'The event listener must be a function.';
|
||||
/**
|
||||
* This object is used to create an `EventEmitter` that, useful for composing
|
||||
* objects that emit events. It implements an interface like `EventTarget` from
|
||||
* DOM Level 2, which is implemented by Node objects in implementations that
|
||||
* support the DOM Event Model.
|
||||
* @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
|
||||
* @see http://nodejs.org/api.html#EventEmitter
|
||||
* @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html
|
||||
*/
|
||||
const eventEmitter = {
|
||||
/**
|
||||
* Registers an event `listener` that is called every time events of
|
||||
* specified `type` are emitted.
|
||||
* @param {String} type
|
||||
* The type of event.
|
||||
* @param {Function} listener
|
||||
* The listener function that processes the event.
|
||||
* @example
|
||||
* worker.on('message', function (data) {
|
||||
* console.log('data received: ' + data)
|
||||
* })
|
||||
*/
|
||||
on: function on(type, listener) {
|
||||
if ('function' !== typeof listener)
|
||||
throw new Error(BAD_LISTENER);
|
||||
let listeners = this._listeners(type);
|
||||
if (0 > listeners.indexOf(listener))
|
||||
listeners.push(listener);
|
||||
// Use of `_public` is required by the legacy traits code that will go away
|
||||
// once bug-637633 is fixed.
|
||||
return this._public || this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers an event `listener` that is called once the next time an event
|
||||
* of the specified `type` is emitted.
|
||||
* @param {String} type
|
||||
* The type of the event.
|
||||
* @param {Function} listener
|
||||
* The listener function that processes the event.
|
||||
*/
|
||||
once: function once(type, listener) {
|
||||
this.on(type, function selfRemovableListener() {
|
||||
this.removeListener(type, selfRemovableListener);
|
||||
listener.apply(this, arguments);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister `listener` for the specified event type.
|
||||
* @param {String} type
|
||||
* The type of event.
|
||||
* @param {Function} listener
|
||||
* The listener function that processes the event.
|
||||
*/
|
||||
removeListener: function removeListener(type, listener) {
|
||||
if ('function' !== typeof listener)
|
||||
throw new Error(BAD_LISTENER);
|
||||
let listeners = this._listeners(type),
|
||||
index = listeners.indexOf(listener);
|
||||
if (0 <= index)
|
||||
listeners.splice(index, 1);
|
||||
// Use of `_public` is required by the legacy traits code, that will go away
|
||||
// once bug-637633 is fixed.
|
||||
return this._public || this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hash of listeners on this EventEmitter.
|
||||
*/
|
||||
_events: null,
|
||||
|
||||
/**
|
||||
* Returns an array of listeners for the specified event `type`. This array
|
||||
* can be manipulated, e.g. to remove listeners.
|
||||
* @param {String} type
|
||||
* The type of event.
|
||||
*/
|
||||
_listeners: function listeners(type) {
|
||||
let events = this._events || (this._events = {});
|
||||
return (events.hasOwnProperty(type) && events[type]) || (events[type] = []);
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute each of the listeners in order with the supplied arguments.
|
||||
* Returns `true` if listener for this event was called, `false` if there are
|
||||
* no listeners for this event `type`.
|
||||
*
|
||||
* All the exceptions that are thrown by listeners during the emit
|
||||
* are caught and can be handled by listeners of 'error' event. Thrown
|
||||
* exceptions are passed as an argument to an 'error' event listener.
|
||||
* If no 'error' listener is registered exception will propagate to a
|
||||
* caller of this method.
|
||||
*
|
||||
* **It's recommended to have a default 'error' listener in all the complete
|
||||
* composition that in worst case may dump errors to the console.**
|
||||
*
|
||||
* @param {String} type
|
||||
* The type of event.
|
||||
* @params {Object|Number|String|Boolean}
|
||||
* Arguments that will be passed to listeners.
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
_emit: function _emit(type, event) {
|
||||
let args = Array.slice(arguments);
|
||||
// Use of `_public` is required by the legacy traits code that will go away
|
||||
// once bug-637633 is fixed.
|
||||
args.unshift(this._public || this);
|
||||
return this._emitOnObject.apply(this, args);
|
||||
},
|
||||
|
||||
/**
|
||||
* A version of _emit that lets you specify the object on which listeners are
|
||||
* called. This is a hack that is sometimes necessary when such an object
|
||||
* (exports, for example) cannot be an EventEmitter for some reason, but other
|
||||
* object(s) managing events for the object are EventEmitters. Once bug
|
||||
* 577782 is fixed, this method shouldn't be necessary.
|
||||
*
|
||||
* @param {object} targetObj
|
||||
* The object on which listeners will be called.
|
||||
* @param {string} type
|
||||
* The event name.
|
||||
* @param {value} event
|
||||
* The first argument to pass to listeners.
|
||||
* @param {value} ...
|
||||
* More arguments to pass to listeners.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_emitOnObject: function _emitOnObject(targetObj, type, event /* , ... */) {
|
||||
let listeners = this._listeners(type).slice(0);
|
||||
// If there is no 'error' event listener then throw.
|
||||
if (type === ERROR_TYPE && !listeners.length)
|
||||
console.exception(event);
|
||||
if (!listeners.length)
|
||||
return false;
|
||||
let params = Array.slice(arguments, 2);
|
||||
for (let listener of listeners) {
|
||||
try {
|
||||
listener.apply(targetObj, params);
|
||||
} catch(e) {
|
||||
// Bug 726967: Ignore exceptions being throws while notifying the error
|
||||
// in order to avoid infinite loops.
|
||||
if (type !== ERROR_TYPE)
|
||||
this._emit(ERROR_TYPE, e);
|
||||
else
|
||||
console.exception("Exception in error event listener " + e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes all the event listeners for the specified event `type`.
|
||||
* @param {String} type
|
||||
* The type of event.
|
||||
*/
|
||||
_removeAllListeners: function _removeAllListeners(type) {
|
||||
if (typeof type == "undefined") {
|
||||
this._events = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
this._listeners(type).splice(0);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
exports.EventEmitter = require("./traits").Trait.compose(eventEmitter);
|
||||
exports.EventEmitterTrait = require('./light-traits').Trait(eventEmitter);
|
@ -1,15 +1,13 @@
|
||||
/* 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 { Class } = require("../../core/heritage");
|
||||
const { removeListener, on } = require("../../dom/events");
|
||||
|
||||
/**
|
||||
* Trait may be used for building objects / composing traits that wish to handle
|
||||
* multiple dom events from multiple event targets in one place. Event targets
|
||||
* Event targets
|
||||
* can be added / removed by calling `observe / ignore` methods. Composer should
|
||||
* provide array of event types it wishes to handle as property
|
||||
* `supportedEventsTypes` and function for handling all those events as
|
||||
|
@ -1,599 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
// `var` is being used in the module in order to make it reusable in
|
||||
// environments in which `let` is not yet supported.
|
||||
|
||||
// Shortcut to `Object.prototype.hasOwnProperty.call`.
|
||||
// owns(object, name) would be the same as
|
||||
// Object.prototype.hasOwnProperty.call(object, name);
|
||||
var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty);
|
||||
|
||||
/**
|
||||
* Whether or not given property descriptors are equivalent. They are
|
||||
* equivalent either if both are marked as 'conflict' or 'required' property
|
||||
* or if all the properties of descriptors are equal.
|
||||
* @param {Object} actual
|
||||
* @param {Object} expected
|
||||
*/
|
||||
function equivalentDescriptors(actual, expected) {
|
||||
return (actual.conflict && expected.conflict) ||
|
||||
(actual.required && expected.required) ||
|
||||
equalDescriptors(actual, expected);
|
||||
}
|
||||
/**
|
||||
* Whether or not given property descriptors define equal properties.
|
||||
*/
|
||||
function equalDescriptors(actual, expected) {
|
||||
return actual.get === expected.get &&
|
||||
actual.set === expected.set &&
|
||||
actual.value === expected.value &&
|
||||
!!actual.enumerable === !!expected.enumerable &&
|
||||
!!actual.configurable === !!expected.configurable &&
|
||||
!!actual.writable === !!expected.writable;
|
||||
}
|
||||
|
||||
// Utilities that throwing exceptions for a properties that are marked
|
||||
// as "required" or "conflict" properties.
|
||||
function throwConflictPropertyError(name) {
|
||||
throw new Error("Remaining conflicting property: `" + name + "`");
|
||||
}
|
||||
function throwRequiredPropertyError(name) {
|
||||
throw new Error("Missing required property: `" + name + "`");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates custom **required** property descriptor. Descriptor contains
|
||||
* non-standard property `required` that is equal to `true`.
|
||||
* @param {String} name
|
||||
* property name to generate descriptor for.
|
||||
* @returns {Object}
|
||||
* custom property descriptor
|
||||
*/
|
||||
function RequiredPropertyDescriptor(name) {
|
||||
// Creating function by binding first argument to a property `name` on the
|
||||
// `throwConflictPropertyError` function. Created function is used as a
|
||||
// getter & setter of the created property descriptor. This way we ensure
|
||||
// that we throw exception late (on property access) if object with
|
||||
// `required` property was instantiated using built-in `Object.create`.
|
||||
var accessor = throwRequiredPropertyError.bind(null, name);
|
||||
return { get: accessor, set: accessor, required: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates custom **conflicting** property descriptor. Descriptor contains
|
||||
* non-standard property `conflict` that is equal to `true`.
|
||||
* @param {String} name
|
||||
* property name to generate descriptor for.
|
||||
* @returns {Object}
|
||||
* custom property descriptor
|
||||
*/
|
||||
function ConflictPropertyDescriptor(name) {
|
||||
// For details see `RequiredPropertyDescriptor` since idea is same.
|
||||
var accessor = throwConflictPropertyError.bind(null, name);
|
||||
return { get: accessor, set: accessor, conflict: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if property is marked as `required` property.
|
||||
*/
|
||||
function isRequiredProperty(object, name) {
|
||||
return !!object[name].required;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if property is marked as `conflict` property.
|
||||
*/
|
||||
function isConflictProperty(object, name) {
|
||||
return !!object[name].conflict;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function tests whether or not method of the `source` object with a given
|
||||
* `name` is inherited from `Object.prototype`.
|
||||
*/
|
||||
function isBuiltInMethod(name, source) {
|
||||
var target = Object.prototype[name];
|
||||
|
||||
// If methods are equal then we know it's `true`.
|
||||
return target == source ||
|
||||
// If `source` object comes form a different sandbox `==` will evaluate
|
||||
// to `false`, in that case we check if functions names and sources match.
|
||||
(String(target) === String(source) && target.name === source.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function overrides `toString` and `constructor` methods of a given `target`
|
||||
* object with a same-named methods of a given `source` if methods of `target`
|
||||
* object are inherited / copied from `Object.prototype`.
|
||||
* @see create
|
||||
*/
|
||||
function overrideBuiltInMethods(target, source) {
|
||||
if (isBuiltInMethod("toString", target.toString)) {
|
||||
Object.defineProperty(target, "toString", {
|
||||
value: source.toString,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
if (isBuiltInMethod("constructor", target.constructor)) {
|
||||
Object.defineProperty(target, "constructor", {
|
||||
value: source.constructor,
|
||||
configurable: true,
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new trait with the same own properties as the original trait,
|
||||
* except that all property names appearing in the first argument are replaced
|
||||
* by "required" property descriptors.
|
||||
* @param {String[]} keys
|
||||
* Array of strings property names.
|
||||
* @param {Object} trait
|
||||
* A trait some properties of which should be excluded.
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* var newTrait = exclude(["name", ...], trait)
|
||||
*/
|
||||
function exclude(names, trait) {
|
||||
var map = {};
|
||||
|
||||
Object.keys(trait).forEach(function(name) {
|
||||
|
||||
// If property is not excluded (the array of names does not contain it),
|
||||
// or it is a "required" property, copy it to the property descriptor `map`
|
||||
// that will be used for creation of resulting trait.
|
||||
if (!~names.indexOf(name) || isRequiredProperty(trait, name))
|
||||
map[name] = { value: trait[name], enumerable: true };
|
||||
|
||||
// For all the `names` in the exclude name array we create required
|
||||
// property descriptors and copy them to the `map`.
|
||||
else
|
||||
map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true };
|
||||
});
|
||||
|
||||
return Object.create(Trait.prototype, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new instance of `Trait` with a properties of a given `trait`,
|
||||
* except that all properties whose name is an own property of `renames` will
|
||||
* be renamed to `renames[name]` and a `"required"` property for name will be
|
||||
* added instead.
|
||||
*
|
||||
* For each renamed property, a required property is generated. If
|
||||
* the `renames` map two properties to the same name, a conflict is generated.
|
||||
* If the `renames` map a property to an existing unrenamed property, a
|
||||
* conflict is generated.
|
||||
*
|
||||
* @param {Object} renames
|
||||
* An object whose own properties serve as a mapping from old names to new
|
||||
* names.
|
||||
* @param {Object} trait
|
||||
* A new trait with renamed properties.
|
||||
* @returns {Object}
|
||||
* @example
|
||||
*
|
||||
* // Return trait with `bar` property equal to `trait.foo` and with
|
||||
* // `foo` and `baz` "required" properties.
|
||||
* var renamedTrait = rename({ foo: "bar", baz: null }), trait);
|
||||
*
|
||||
* // t1 and t2 are equivalent traits
|
||||
* var t1 = rename({a: "b"}, t);
|
||||
* var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] });
|
||||
*/
|
||||
function rename(renames, trait) {
|
||||
var map = {};
|
||||
|
||||
// Loop over all the properties of the given `trait` and copy them to a
|
||||
// property descriptor `map` that will be used for the creation of the
|
||||
// resulting trait. Also, rename properties in the `map` as specified by
|
||||
// `renames`.
|
||||
Object.keys(trait).forEach(function(name) {
|
||||
var alias;
|
||||
|
||||
// If the property is in the `renames` map, and it isn't a "required"
|
||||
// property (which should never need to be aliased because "required"
|
||||
// properties never conflict), then we must try to rename it.
|
||||
if (owns(renames, name) && !isRequiredProperty(trait, name)) {
|
||||
alias = renames[name];
|
||||
|
||||
// If the `map` already has the `alias`, and it isn't a "required"
|
||||
// property, that means the `alias` conflicts with an existing name for a
|
||||
// provided trait (that can happen if >=2 properties are aliased to the
|
||||
// same name). In this case we mark it as a conflicting property.
|
||||
// Otherwise, everything is fine, and we copy property with an `alias`
|
||||
// name.
|
||||
if (owns(map, alias) && !map[alias].value.required) {
|
||||
map[alias] = {
|
||||
value: ConflictPropertyDescriptor(alias),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
else {
|
||||
map[alias] = {
|
||||
value: trait[name],
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
|
||||
// Regardless of whether or not the rename was successful, we check to
|
||||
// see if the original `name` exists in the map (such a property
|
||||
// could exist if previous another property was aliased to this `name`).
|
||||
// If it isn't, we mark it as "required", to make sure the caller
|
||||
// provides another value for the old name, which methods of the trait
|
||||
// might continue to reference.
|
||||
if (!owns(map, name)) {
|
||||
map[name] = {
|
||||
value: RequiredPropertyDescriptor(name),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, either the property isn't in the `renames` map (thus the
|
||||
// caller is not trying to rename it) or it is a "required" property.
|
||||
// Either way, we don't have to alias the property, we just have to copy it
|
||||
// to the map.
|
||||
else {
|
||||
// The property isn't in the map yet, so we copy it over.
|
||||
if (!owns(map, name)) {
|
||||
map[name] = { value: trait[name], enumerable: true };
|
||||
}
|
||||
|
||||
// The property is already in the map (that means another property was
|
||||
// aliased with this `name`, which creates a conflict if the property is
|
||||
// not marked as "required"), so we have to mark it as a "conflict"
|
||||
// property.
|
||||
else if (!isRequiredProperty(trait, name)) {
|
||||
map[name] = {
|
||||
value: ConflictPropertyDescriptor(name),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
return Object.create(Trait.prototype, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new resolved trait, with all the same properties as the original
|
||||
* `trait`, except that all properties whose name is an own property of
|
||||
* `resolutions` will be renamed to `resolutions[name]`.
|
||||
*
|
||||
* If `resolutions[name]` is `null`, the value is mapped to a property
|
||||
* descriptor that is marked as a "required" property.
|
||||
*/
|
||||
function resolve(resolutions, trait) {
|
||||
var renames = {};
|
||||
var exclusions = [];
|
||||
|
||||
// Go through each mapping in `resolutions` object and distribute it either
|
||||
// to `renames` or `exclusions`.
|
||||
Object.keys(resolutions).forEach(function(name) {
|
||||
|
||||
// If `resolutions[name]` is a truthy value then it's a mapping old -> new
|
||||
// so we copy it to `renames` map.
|
||||
if (resolutions[name])
|
||||
renames[name] = resolutions[name];
|
||||
|
||||
// Otherwise it's not a mapping but an exclusion instead in which case we
|
||||
// add it to the `exclusions` array.
|
||||
else
|
||||
exclusions.push(name);
|
||||
});
|
||||
|
||||
// First `exclude` **then** `rename` and order is important since
|
||||
// `exclude` and `rename` are not associative.
|
||||
return rename(renames, exclude(exclusions, trait));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Trait (a custom property descriptor map) that represents the given
|
||||
* `object`'s own properties. Property descriptor map is a "custom", because it
|
||||
* inherits from `Trait.prototype` and it's property descriptors may contain
|
||||
* two attributes that is not part of the ES5 specification:
|
||||
*
|
||||
* - "required" (this property must be provided by another trait
|
||||
* before an instance of this trait can be created)
|
||||
* - "conflict" (when the trait is composed with another trait,
|
||||
* a unique value for this property is provided by two or more traits)
|
||||
*
|
||||
* Data properties bound to the `Trait.required` singleton exported by
|
||||
* this module will be marked as "required" properties.
|
||||
*
|
||||
* @param {Object} object
|
||||
* Map of properties to compose trait from.
|
||||
* @returns {Trait}
|
||||
* Trait / Property descriptor map containing all the own properties of the
|
||||
* given argument.
|
||||
*/
|
||||
function trait(object) {
|
||||
var map;
|
||||
var trait = object;
|
||||
|
||||
if (!(object instanceof Trait)) {
|
||||
// If the passed `object` is not already an instance of `Trait`, we create
|
||||
// a property descriptor `map` containing descriptors for the own properties
|
||||
// of the given `object`. `map` is then used to create a `Trait` instance
|
||||
// after all properties are mapped. Note that we can't create a trait and
|
||||
// then just copy properties into it since that will fail for inherited
|
||||
// read-only properties.
|
||||
map = {};
|
||||
|
||||
// Each own property of the given `object` is mapped to a data property
|
||||
// whose value is a property descriptor.
|
||||
Object.keys(object).forEach(function (name) {
|
||||
|
||||
// If property of an `object` is equal to a `Trait.required`, it means
|
||||
// that it was marked as "required" property, in which case we map it
|
||||
// to "required" property.
|
||||
if (Trait.required ==
|
||||
Object.getOwnPropertyDescriptor(object, name).value) {
|
||||
map[name] = {
|
||||
value: RequiredPropertyDescriptor(name),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
// Otherwise property is mapped to it's property descriptor.
|
||||
else {
|
||||
map[name] = {
|
||||
value: Object.getOwnPropertyDescriptor(object, name),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
trait = Object.create(Trait.prototype, map);
|
||||
}
|
||||
return trait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose a property descriptor map that inherits from `Trait.prototype` and
|
||||
* contains property descriptors for all the own properties of the passed
|
||||
* traits.
|
||||
*
|
||||
* If two or more traits have own properties with the same name, the returned
|
||||
* trait will contain a "conflict" property for that name. Composition is a
|
||||
* commutative and associative operation, and the order of its arguments is
|
||||
* irrelevant.
|
||||
*/
|
||||
function compose(trait1, trait2/*, ...*/) {
|
||||
// Create a new property descriptor `map` to which all the own properties
|
||||
// of the passed traits are copied. This map will be used to create a `Trait`
|
||||
// instance that will be the result of this composition.
|
||||
var map = {};
|
||||
|
||||
// Properties of each passed trait are copied to the composition.
|
||||
Array.prototype.forEach.call(arguments, function(trait) {
|
||||
// Copying each property of the given trait.
|
||||
Object.keys(trait).forEach(function(name) {
|
||||
|
||||
// If `map` already owns a property with the `name` and it is not
|
||||
// marked "required".
|
||||
if (owns(map, name) && !map[name].value.required) {
|
||||
|
||||
// If the source trait's property with the `name` is marked as
|
||||
// "required", we do nothing, as the requirement was already resolved
|
||||
// by a property in the `map` (because it already contains a
|
||||
// non-required property with that `name`). But if properties are just
|
||||
// different, we have a name clash and we substitute it with a property
|
||||
// that is marked "conflict".
|
||||
if (!isRequiredProperty(trait, name) &&
|
||||
!equivalentDescriptors(map[name].value, trait[name])
|
||||
) {
|
||||
map[name] = {
|
||||
value: ConflictPropertyDescriptor(name),
|
||||
enumerable: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, the `map` does not have an own property with the `name`, or
|
||||
// it is marked "required". Either way, the trait's property is copied to
|
||||
// the map (if the property of the `map` is marked "required", it is going
|
||||
// to be resolved by the property that is being copied).
|
||||
else {
|
||||
map[name] = { value: trait[name], enumerable: true };
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Object.create(Trait.prototype, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* `defineProperties` is like `Object.defineProperties`, except that it
|
||||
* ensures that:
|
||||
* - An exception is thrown if any property in a given `properties` map
|
||||
* is marked as "required" property and same named property is not
|
||||
* found in a given `prototype`.
|
||||
* - An exception is thrown if any property in a given `properties` map
|
||||
* is marked as "conflict" property.
|
||||
* @param {Object} object
|
||||
* Object to define properties on.
|
||||
* @param {Object} properties
|
||||
* Properties descriptor map.
|
||||
* @returns {Object}
|
||||
* `object` that was passed as a first argument.
|
||||
*/
|
||||
function defineProperties(object, properties) {
|
||||
|
||||
// Create a map into which we will copy each verified property from the given
|
||||
// `properties` description map. We use it to verify that none of the
|
||||
// provided properties is marked as a "conflict" property and that all
|
||||
// "required" properties are resolved by a property of an `object`, so we
|
||||
// can throw an exception before mutating object if that isn't the case.
|
||||
var verifiedProperties = {};
|
||||
|
||||
// Coping each property from a given `properties` descriptor map to a
|
||||
// verified map of property descriptors.
|
||||
Object.keys(properties).forEach(function(name) {
|
||||
|
||||
// If property is marked as "required" property and we don't have a same
|
||||
// named property in a given `object` we throw an exception. If `object`
|
||||
// has same named property just skip this property since required property
|
||||
// is was inherited and there for requirement was satisfied.
|
||||
if (isRequiredProperty(properties, name)) {
|
||||
if (!(name in object))
|
||||
throwRequiredPropertyError(name);
|
||||
}
|
||||
|
||||
// If property is marked as "conflict" property we throw an exception.
|
||||
else if (isConflictProperty(properties, name)) {
|
||||
throwConflictPropertyError(name);
|
||||
}
|
||||
|
||||
// If property is not marked neither as "required" nor "conflict" property
|
||||
// we copy it to verified properties map.
|
||||
else {
|
||||
verifiedProperties[name] = properties[name];
|
||||
}
|
||||
});
|
||||
|
||||
// If no exceptions were thrown yet, we know that our verified property
|
||||
// descriptor map has no properties marked as "conflict" or "required",
|
||||
// so we just delegate to the built-in `Object.defineProperties`.
|
||||
return Object.defineProperties(object, verifiedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* `create` is like `Object.create`, except that it ensures that:
|
||||
* - An exception is thrown if any property in a given `properties` map
|
||||
* is marked as "required" property and same named property is not
|
||||
* found in a given `prototype`.
|
||||
* - An exception is thrown if any property in a given `properties` map
|
||||
* is marked as "conflict" property.
|
||||
* @param {Object} prototype
|
||||
* prototype of the composed object
|
||||
* @param {Object} properties
|
||||
* Properties descriptor map.
|
||||
* @returns {Object}
|
||||
* An object that inherits form a given `prototype` and implements all the
|
||||
* properties defined by a given `properties` descriptor map.
|
||||
*/
|
||||
function create(prototype, properties) {
|
||||
|
||||
// Creating an instance of the given `prototype`.
|
||||
var object = Object.create(prototype);
|
||||
|
||||
// Overriding `toString`, `constructor` methods if they are just inherited
|
||||
// from `Object.prototype` with a same named methods of the `Trait.prototype`
|
||||
// that will have more relevant behavior.
|
||||
overrideBuiltInMethods(object, Trait.prototype);
|
||||
|
||||
// Trying to define given `properties` on the `object`. We use our custom
|
||||
// `defineProperties` function instead of build-in `Object.defineProperties`
|
||||
// that behaves exactly the same, except that it will throw if any
|
||||
// property in the given `properties` descriptor is marked as "required" or
|
||||
// "conflict" property.
|
||||
return defineProperties(object, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new trait. If two or more traits have own properties with the
|
||||
* same name, the new trait will contain a "conflict" property for that name.
|
||||
* "compose" is a commutative and associative operation, and the order of its
|
||||
* arguments is not significant.
|
||||
*
|
||||
* **Note:** Use `Trait.compose` instead of calling this function with more
|
||||
* than one argument. The multiple-argument functionality is strictly for
|
||||
* backward compatibility.
|
||||
*
|
||||
* @params {Object} trait
|
||||
* Takes traits as an arguments
|
||||
* @returns {Object}
|
||||
* New trait containing the combined own properties of all the traits.
|
||||
* @example
|
||||
* var newTrait = compose(trait_1, trait_2, ..., trait_N)
|
||||
*/
|
||||
function Trait(trait1, trait2) {
|
||||
|
||||
// If the function was called with one argument, the argument should be
|
||||
// an object whose properties are mapped to property descriptors on a new
|
||||
// instance of Trait, so we delegate to the trait function.
|
||||
// If the function was called with more than one argument, those arguments
|
||||
// should be instances of Trait or plain property descriptor maps
|
||||
// whose properties should be mixed into a new instance of Trait,
|
||||
// so we delegate to the compose function.
|
||||
|
||||
return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments);
|
||||
}
|
||||
|
||||
Object.freeze(Object.defineProperties(Trait.prototype, {
|
||||
toString: {
|
||||
value: function toString() {
|
||||
return "[object " + this.constructor.name + "]";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* `create` is like `Object.create`, except that it ensures that:
|
||||
* - An exception is thrown if this trait defines a property that is
|
||||
* marked as required property and same named property is not
|
||||
* found in a given `prototype`.
|
||||
* - An exception is thrown if this trait contains property that is
|
||||
* marked as "conflict" property.
|
||||
* @param {Object}
|
||||
* prototype of the compared object
|
||||
* @returns {Object}
|
||||
* An object with all of the properties described by the trait.
|
||||
*/
|
||||
create: {
|
||||
value: function createTrait(prototype) {
|
||||
return create(undefined === prototype ? Object.prototype : prototype,
|
||||
this);
|
||||
},
|
||||
enumerable: true
|
||||
},
|
||||
|
||||
/**
|
||||
* Composes a new resolved trait, with all the same properties as the original
|
||||
* trait, except that all properties whose name is an own property of
|
||||
* `resolutions` will be renamed to the value of `resolutions[name]`. If
|
||||
* `resolutions[name]` is `null`, the property is marked as "required".
|
||||
* @param {Object} resolutions
|
||||
* An object whose own properties serve as a mapping from old names to new
|
||||
* names, or to `null` if the property should be excluded.
|
||||
* @returns {Object}
|
||||
* New trait with the same own properties as the original trait but renamed.
|
||||
*/
|
||||
resolve: {
|
||||
value: function resolveTrait(resolutions) {
|
||||
return resolve(resolutions, this);
|
||||
},
|
||||
enumerable: true
|
||||
}
|
||||
}));
|
||||
|
||||
/**
|
||||
* @see compose
|
||||
*/
|
||||
Trait.compose = Object.freeze(compose);
|
||||
Object.freeze(compose.prototype);
|
||||
|
||||
/**
|
||||
* Constant singleton, representing placeholder for required properties.
|
||||
* @type {Object}
|
||||
*/
|
||||
Trait.required = Object.freeze(Object.create(Object.prototype, {
|
||||
toString: {
|
||||
value: Object.freeze(function toString() {
|
||||
return "<Trait.required>";
|
||||
})
|
||||
}
|
||||
}));
|
||||
Object.freeze(Trait.required.toString.prototype);
|
||||
|
||||
exports.Trait = Object.freeze(Trait);
|
@ -1,125 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
};
|
||||
|
||||
const { Trait } = require('../deprecated/traits');
|
||||
|
||||
/**
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/util_list
|
||||
*/
|
||||
const Iterable = Trait.compose({
|
||||
/**
|
||||
* Hash map of key-values to iterate over.
|
||||
* Note: That this property can be a getter if you need dynamic behavior.
|
||||
* @type {Object}
|
||||
*/
|
||||
_keyValueMap: Trait.required,
|
||||
/**
|
||||
* Custom iterator providing `Iterable`s enumeration behavior.
|
||||
* @param {Boolean} onKeys
|
||||
*/
|
||||
__iterator__: function __iterator__(onKeys, onKeyValue) {
|
||||
let map = this._keyValueMap;
|
||||
for (let key in map)
|
||||
yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key];
|
||||
}
|
||||
});
|
||||
exports.Iterable = Iterable;
|
||||
|
||||
/**
|
||||
* An ordered collection (also known as a sequence) disallowing duplicate
|
||||
* elements. List is composed out of `Iterable` there for it provides custom
|
||||
* enumeration behavior that is similar to array (enumerates only on the
|
||||
* elements of the list). List is a base trait and is meant to be a part of
|
||||
* composition, since all of it's API is private except length property.
|
||||
*/
|
||||
const listOptions = {
|
||||
_keyValueMap: null,
|
||||
/**
|
||||
* List constructor can take any number of element to populate itself.
|
||||
* @params {Object|String|Number} element
|
||||
* @example
|
||||
* List(1,2,3).length == 3 // true
|
||||
*/
|
||||
constructor: function List() {
|
||||
this._keyValueMap = [];
|
||||
for (let i = 0, ii = arguments.length; i < ii; i++)
|
||||
this._add(arguments[i]);
|
||||
},
|
||||
/**
|
||||
* Number of elements in this list.
|
||||
* @type {Number}
|
||||
*/
|
||||
get length() this._keyValueMap.length,
|
||||
/**
|
||||
* Returns a string representing this list.
|
||||
* @returns {String}
|
||||
*/
|
||||
toString: function toString() 'List(' + this._keyValueMap + ')',
|
||||
/**
|
||||
* Returns `true` if this list contains the specified `element`.
|
||||
* @param {Object|Number|String} element
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
_has: function _has(element) 0 <= this._keyValueMap.indexOf(element),
|
||||
/**
|
||||
* Appends the specified `element` to the end of this list, if it doesn't
|
||||
* contains it. Ignores the call if `element` is already contained.
|
||||
* @param {Object|Number|String} element
|
||||
*/
|
||||
_add: function _add(element) {
|
||||
let list = this._keyValueMap,
|
||||
index = list.indexOf(element);
|
||||
if (0 > index)
|
||||
list.push(this._public[list.length] = element);
|
||||
},
|
||||
/**
|
||||
* Removes specified `element` from this list, if it contains it.
|
||||
* Ignores the call if `element` is not contained.
|
||||
* @param {Object|Number|String} element
|
||||
*/
|
||||
_remove: function _remove(element) {
|
||||
let list = this._keyValueMap,
|
||||
index = list.indexOf(element);
|
||||
if (0 <= index) {
|
||||
delete this._public[list.length - 1];
|
||||
list.splice(index, 1);
|
||||
for (let length = list.length; index < length; index++)
|
||||
this._public[index] = list[index];
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Removes all of the elements from this list.
|
||||
*/
|
||||
_clear: function _clear() {
|
||||
for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++)
|
||||
delete this._public[i];
|
||||
this._keyValueMap.splice(0);
|
||||
},
|
||||
/**
|
||||
* Custom iterator providing `List`s enumeration behavior.
|
||||
* We cant reuse `_iterator` that is defined by `Iterable` since it provides
|
||||
* iteration in an arbitrary order.
|
||||
* @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in
|
||||
* @param {Boolean} onKeys
|
||||
*/
|
||||
__iterator__: function __iterator__(onKeys, onKeyValue) {
|
||||
let array = this._keyValueMap.slice(0),
|
||||
i = -1;
|
||||
for (let element of array)
|
||||
yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
|
||||
},
|
||||
};
|
||||
listOptions[Symbol.iterator] = function* iterator() {
|
||||
let array = this._keyValueMap.slice(0);
|
||||
|
||||
for (let element of array)
|
||||
yield element;
|
||||
}
|
||||
const List = Trait.resolve({ toString: null }).compose(listOptions);
|
||||
exports.List = List;
|
@ -1,228 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const { Worker } = require('./traits-worker');
|
||||
const { Loader } = require('../content/loader');
|
||||
const hiddenFrames = require('../frame/hidden-frame');
|
||||
const { on, off } = require('../system/events');
|
||||
const unload = require('../system/unload');
|
||||
const { getDocShell } = require("../frame/utils");
|
||||
const { ignoreWindow } = require('../private-browsing/utils');
|
||||
|
||||
// Everything coming from add-on's xpi considered an asset.
|
||||
const assetsURI = require('../self').data.url().replace(/data\/$/, "");
|
||||
|
||||
/**
|
||||
* This trait is layered on top of `Worker` and in contrast to symbiont
|
||||
* Worker constructor requires `content` option that represents content
|
||||
* that will be loaded in the provided frame, if frame is not provided
|
||||
* Worker will create hidden one.
|
||||
*/
|
||||
const Symbiont = Worker.resolve({
|
||||
constructor: '_initWorker',
|
||||
destroy: '_workerDestroy'
|
||||
}).compose(Loader, {
|
||||
|
||||
/**
|
||||
* The constructor requires all the options that are required by
|
||||
* `require('content').Worker` with the difference that the `frame` option
|
||||
* is optional. If `frame` is not provided, `contentURL` is expected.
|
||||
* @param {Object} options
|
||||
* @param {String} options.contentURL
|
||||
* URL of a content to load into `this._frame` and create worker for.
|
||||
* @param {Element} [options.frame]
|
||||
* iframe element that is used to load `options.contentURL` into.
|
||||
* if frame is not provided hidden iframe will be created.
|
||||
*/
|
||||
constructor: function Symbiont(options) {
|
||||
options = options || {};
|
||||
|
||||
if ('contentURL' in options)
|
||||
this.contentURL = options.contentURL;
|
||||
if ('contentScriptWhen' in options)
|
||||
this.contentScriptWhen = options.contentScriptWhen;
|
||||
if ('contentScriptOptions' in options)
|
||||
this.contentScriptOptions = options.contentScriptOptions;
|
||||
if ('contentScriptFile' in options)
|
||||
this.contentScriptFile = options.contentScriptFile;
|
||||
if ('contentScript' in options)
|
||||
this.contentScript = options.contentScript;
|
||||
if ('allow' in options)
|
||||
this.allow = options.allow;
|
||||
if ('onError' in options)
|
||||
this.on('error', options.onError);
|
||||
if ('onMessage' in options)
|
||||
this.on('message', options.onMessage);
|
||||
if ('frame' in options) {
|
||||
this._initFrame(options.frame);
|
||||
}
|
||||
else {
|
||||
let self = this;
|
||||
this._hiddenFrame = hiddenFrames.HiddenFrame({
|
||||
onReady: function onFrame() {
|
||||
self._initFrame(this.element);
|
||||
},
|
||||
onUnload: function onUnload() {
|
||||
// Bug 751211: Remove reference to _frame when hidden frame is
|
||||
// automatically removed on unload, otherwise we are going to face
|
||||
// "dead object" exception
|
||||
self.destroy();
|
||||
}
|
||||
});
|
||||
hiddenFrames.add(this._hiddenFrame);
|
||||
}
|
||||
|
||||
unload.ensure(this._public, "destroy");
|
||||
},
|
||||
|
||||
destroy: function destroy() {
|
||||
this._workerDestroy();
|
||||
this._unregisterListener();
|
||||
this._frame = null;
|
||||
if (this._hiddenFrame) {
|
||||
hiddenFrames.remove(this._hiddenFrame);
|
||||
this._hiddenFrame = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* XUL iframe or browser elements with attribute `type` being `content`.
|
||||
* Used to create `ContentSymbiont` from.
|
||||
* @type {nsIFrame|nsIBrowser}
|
||||
*/
|
||||
_frame: null,
|
||||
|
||||
/**
|
||||
* Listener to the `'frameReady"` event (emitted when `iframe` is ready).
|
||||
* Removes listener, sets right permissions to the frame and loads content.
|
||||
*/
|
||||
_initFrame: function _initFrame(frame) {
|
||||
if (this._loadListener)
|
||||
this._unregisterListener();
|
||||
|
||||
this._frame = frame;
|
||||
|
||||
if (getDocShell(frame)) {
|
||||
this._reallyInitFrame(frame);
|
||||
}
|
||||
else {
|
||||
if (this._waitForFrame) {
|
||||
off('content-document-global-created', this._waitForFrame);
|
||||
}
|
||||
this._waitForFrame = this.__waitForFrame.bind(this, frame);
|
||||
on('content-document-global-created', this._waitForFrame);
|
||||
}
|
||||
},
|
||||
|
||||
__waitForFrame: function _waitForFrame(frame, { subject: win }) {
|
||||
if (frame.contentWindow == win) {
|
||||
off('content-document-global-created', this._waitForFrame);
|
||||
delete this._waitForFrame;
|
||||
this._reallyInitFrame(frame);
|
||||
}
|
||||
},
|
||||
|
||||
_reallyInitFrame: function _reallyInitFrame(frame) {
|
||||
getDocShell(frame).allowJavascript = this.allow.script;
|
||||
frame.setAttribute("src", this._contentURL);
|
||||
|
||||
// Inject `addon` object in document if we load a document from
|
||||
// one of our addon folder and if no content script are defined. bug 612726
|
||||
let isDataResource =
|
||||
typeof this._contentURL == "string" &&
|
||||
this._contentURL.indexOf(assetsURI) == 0;
|
||||
let hasContentScript =
|
||||
(Array.isArray(this.contentScript) ? this.contentScript.length > 0
|
||||
: !!this.contentScript) ||
|
||||
(Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0
|
||||
: !!this.contentScriptFile);
|
||||
// If we have to inject `addon` we have to do it before document
|
||||
// script execution, so during `start`:
|
||||
this._injectInDocument = isDataResource && !hasContentScript;
|
||||
if (this._injectInDocument)
|
||||
this.contentScriptWhen = "start";
|
||||
|
||||
if ((frame.contentDocument.readyState == "complete" ||
|
||||
(frame.contentDocument.readyState == "interactive" &&
|
||||
this.contentScriptWhen != 'end' )) &&
|
||||
frame.contentDocument.location == this._contentURL) {
|
||||
// In some cases src doesn't change and document is already ready
|
||||
// (for ex: when the user moves a widget while customizing toolbars.)
|
||||
this._onInit();
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
|
||||
if ('start' == this.contentScriptWhen) {
|
||||
this._loadEvent = 'start';
|
||||
on('document-element-inserted',
|
||||
this._loadListener = function onStart({ subject: doc }) {
|
||||
let window = doc.defaultView;
|
||||
|
||||
if (ignoreWindow(window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (window && window == frame.contentWindow) {
|
||||
self._unregisterListener();
|
||||
self._onInit();
|
||||
}
|
||||
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded';
|
||||
this._loadEvent = eventName;
|
||||
frame.addEventListener(eventName,
|
||||
this._loadListener = function _onReady(event) {
|
||||
|
||||
if (event.target != frame.contentDocument)
|
||||
return;
|
||||
self._unregisterListener();
|
||||
|
||||
self._onInit();
|
||||
|
||||
}, true);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister listener that watchs for document being ready to be injected.
|
||||
* This listener is registered in `Symbiont._initFrame`.
|
||||
*/
|
||||
_unregisterListener: function _unregisterListener() {
|
||||
if (this._waitForFrame) {
|
||||
off('content-document-global-created', this._waitForFrame);
|
||||
delete this._waitForFrame;
|
||||
}
|
||||
|
||||
if (!this._loadListener)
|
||||
return;
|
||||
if (this._loadEvent == "start") {
|
||||
off('document-element-inserted', this._loadListener);
|
||||
}
|
||||
else {
|
||||
this._frame.removeEventListener(this._loadEvent, this._loadListener,
|
||||
true);
|
||||
}
|
||||
this._loadListener = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by Symbiont itself when the frame is ready to load
|
||||
* content scripts according to contentScriptWhen. Overloaded by Panel.
|
||||
*/
|
||||
_onInit: function () {
|
||||
this._initWorker({ window: this._frame.contentWindow });
|
||||
}
|
||||
|
||||
});
|
||||
exports.Symbiont = Symbiont;
|
@ -90,7 +90,7 @@ const Worker = Class({
|
||||
* argument, which represents data to be sent to the worker. The data may
|
||||
* be any primitive type value or `JSON`. Call of this method asynchronously
|
||||
* emits `message` event with data value in the global scope of this
|
||||
* symbiont.
|
||||
* worker.
|
||||
*
|
||||
* `message` event listeners can be set either by calling
|
||||
* `self.on` with a first argument string `"message"` or by
|
||||
|
@ -1,636 +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/. */
|
||||
|
||||
/**
|
||||
*
|
||||
* `deprecated/traits-worker` was previously `content/worker` and kept
|
||||
* only due to `deprecated/symbiont` using it, which is necessary for
|
||||
* `widget`, until that reaches deprecation EOL.
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const { Trait } = require('./traits');
|
||||
const { EventEmitter, EventEmitterTrait } = require('./events');
|
||||
const { Ci, Cu, Cc } = require('chrome');
|
||||
const timer = require('../timers');
|
||||
const { URL } = require('../url');
|
||||
const unload = require('../system/unload');
|
||||
const observers = require('../system/events');
|
||||
const { Cortex } = require('./cortex');
|
||||
const { sandbox, evaluate, load } = require("../loader/sandbox");
|
||||
const { merge } = require('../util/object');
|
||||
const { getInnerId } = require("../window/utils");
|
||||
const { getTabForWindow } = require('../tabs/helpers');
|
||||
const { getTabForContentWindow } = require('../tabs/utils');
|
||||
|
||||
/* Trick the linker in order to ensure shipping these files in the XPI.
|
||||
require('../content/content-worker.js');
|
||||
Then, retrieve URL of these files in the XPI:
|
||||
*/
|
||||
let prefix = module.uri.split('deprecated/traits-worker.js')[0];
|
||||
const CONTENT_WORKER_URL = prefix + 'content/content-worker.js';
|
||||
|
||||
// Fetch additional list of domains to authorize access to for each content
|
||||
// script. It is stored in manifest `metadata` field which contains
|
||||
// package.json data. This list is originaly defined by authors in
|
||||
// `permissions` attribute of their package.json addon file.
|
||||
const permissions = require('@loader/options').metadata['permissions'] || {};
|
||||
const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || [];
|
||||
|
||||
const JS_VERSION = '1.8';
|
||||
|
||||
const ERR_DESTROYED =
|
||||
"Couldn't find the worker to receive this message. " +
|
||||
"The script may not be initialized yet, or may already have been unloaded.";
|
||||
|
||||
const ERR_FROZEN = "The page is currently hidden and can no longer be used " +
|
||||
"until it is visible again.";
|
||||
|
||||
|
||||
const WorkerSandbox = EventEmitter.compose({
|
||||
|
||||
/**
|
||||
* Emit a message to the worker content sandbox
|
||||
*/
|
||||
emit: function emit() {
|
||||
// First ensure having a regular array
|
||||
// (otherwise, `arguments` would be mapped to an object by `stringify`)
|
||||
let array = Array.slice(arguments);
|
||||
// JSON.stringify is buggy with cross-sandbox values,
|
||||
// it may return "{}" on functions. Use a replacer to match them correctly.
|
||||
function replacer(k, v) {
|
||||
return typeof v === "function" ? undefined : v;
|
||||
}
|
||||
// Ensure having an asynchronous behavior
|
||||
let self = this;
|
||||
timer.setTimeout(function () {
|
||||
self._emitToContent(JSON.stringify(array, replacer));
|
||||
}, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronous version of `emit`.
|
||||
* /!\ Should only be used when it is strictly mandatory /!\
|
||||
* Doesn't ensure passing only JSON values.
|
||||
* Mainly used by context-menu in order to avoid breaking it.
|
||||
*/
|
||||
emitSync: function emitSync() {
|
||||
let args = Array.slice(arguments);
|
||||
return this._emitToContent(Cu.cloneInto(args, this._addonWorker._window));
|
||||
},
|
||||
|
||||
/**
|
||||
* Method called by the worker sandbox when it needs to send a message
|
||||
*/
|
||||
_onContentEvent: function onContentEvent(args) {
|
||||
// As `emit`, we ensure having an asynchronous behavior
|
||||
let self = this;
|
||||
timer.setTimeout(function () {
|
||||
// We emit event to chrome/addon listeners
|
||||
self._emit.apply(self, JSON.parse(args));
|
||||
}, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures sandbox and loads content scripts into it.
|
||||
* @param {Worker} worker
|
||||
* content worker
|
||||
*/
|
||||
constructor: function WorkerSandbox(worker) {
|
||||
this._addonWorker = worker;
|
||||
|
||||
// Ensure that `emit` has always the right `this`
|
||||
this.emit = this.emit.bind(this);
|
||||
this.emitSync = this.emitSync.bind(this);
|
||||
|
||||
// We receive a wrapped window, that may be an xraywrapper if it's content
|
||||
let window = worker._window;
|
||||
let proto = window;
|
||||
|
||||
// Eventually use expanded principal sandbox feature, if some are given.
|
||||
//
|
||||
// But prevent it when the Worker isn't used for a content script but for
|
||||
// injecting `addon` object into a Panel, Widget, ... scope.
|
||||
// That's because:
|
||||
// 1/ It is useless to use multiple domains as the worker is only used
|
||||
// to communicate with the addon,
|
||||
// 2/ By using it it would prevent the document to have access to any JS
|
||||
// value of the worker. As JS values coming from multiple domain principals
|
||||
// can't be accessed by "mono-principals" (principal with only one domain).
|
||||
// Even if this principal is for a domain that is specified in the multiple
|
||||
// domain principal.
|
||||
let principals = window;
|
||||
let wantGlobalProperties = []
|
||||
if (EXPANDED_PRINCIPALS.length > 0 && !worker._injectInDocument) {
|
||||
principals = EXPANDED_PRINCIPALS.concat(window);
|
||||
// We have to replace XHR constructor of the content document
|
||||
// with a custom cross origin one, automagically added by platform code:
|
||||
delete proto.XMLHttpRequest;
|
||||
wantGlobalProperties.push("XMLHttpRequest");
|
||||
}
|
||||
|
||||
// Create the sandbox and bind it to window in order for content scripts to
|
||||
// have access to all standard globals (window, document, ...)
|
||||
let content = this._sandbox = sandbox(principals, {
|
||||
sandboxPrototype: proto,
|
||||
wantXrays: !worker._injectInDocument,
|
||||
wantGlobalProperties: wantGlobalProperties,
|
||||
sameZoneAs: window,
|
||||
metadata: {
|
||||
SDKContentScript: true,
|
||||
'inner-window-id': getInnerId(window)
|
||||
}
|
||||
});
|
||||
// We have to ensure that window.top and window.parent are the exact same
|
||||
// object than window object, i.e. the sandbox global object. But not
|
||||
// always, in case of iframes, top and parent are another window object.
|
||||
let top = window.top === window ? content : content.top;
|
||||
let parent = window.parent === window ? content : content.parent;
|
||||
merge(content, {
|
||||
// We need "this === window === top" to be true in toplevel scope:
|
||||
get window() content,
|
||||
get top() top,
|
||||
get parent() parent
|
||||
});
|
||||
// Use the Greasemonkey naming convention to provide access to the
|
||||
// unwrapped window object so the content script can access document
|
||||
// JavaScript values.
|
||||
// NOTE: this functionality is experimental and may change or go away
|
||||
// at any time!
|
||||
//
|
||||
// Note that because waivers aren't propagated between origins, we
|
||||
// need the unsafeWindow getter to live in the sandbox.
|
||||
var unsafeWindowGetter =
|
||||
new content.Function('return window.wrappedJSObject || window;');
|
||||
Object.defineProperty(content, 'unsafeWindow', {get: unsafeWindowGetter});
|
||||
|
||||
|
||||
// Load trusted code that will inject content script API.
|
||||
let ContentWorker = load(content, CONTENT_WORKER_URL);
|
||||
|
||||
// prepare a clean `self.options`
|
||||
let options = 'contentScriptOptions' in worker ?
|
||||
JSON.stringify( worker.contentScriptOptions ) :
|
||||
undefined;
|
||||
|
||||
// Then call `inject` method and communicate with this script
|
||||
// by trading two methods that allow to send events to the other side:
|
||||
// - `onEvent` called by content script
|
||||
// - `result.emitToContent` called by addon script
|
||||
let chromeAPI = Cu.cloneInto({
|
||||
timers: {
|
||||
setTimeout: timer.setTimeout.bind(timer),
|
||||
setInterval: timer.setInterval.bind(timer),
|
||||
clearTimeout: timer.clearTimeout.bind(timer),
|
||||
clearInterval: timer.clearInterval.bind(timer),
|
||||
},
|
||||
sandbox: {
|
||||
evaluate: evaluate,
|
||||
},
|
||||
}, ContentWorker, {cloneFunctions: true});
|
||||
let onEvent = Cu.exportFunction(this._onContentEvent.bind(this), ContentWorker);
|
||||
let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);
|
||||
this._emitToContent = result;
|
||||
|
||||
// Handle messages send by this script:
|
||||
let self = this;
|
||||
// console.xxx calls
|
||||
this.on("console", function consoleListener(kind) {
|
||||
console[kind].apply(console, Array.slice(arguments, 1));
|
||||
});
|
||||
|
||||
// self.postMessage calls
|
||||
this.on("message", function postMessage(data) {
|
||||
// destroyed?
|
||||
if (self._addonWorker)
|
||||
self._addonWorker._emit('message', data);
|
||||
});
|
||||
|
||||
// self.port.emit calls
|
||||
this.on("event", function portEmit(name, args) {
|
||||
// destroyed?
|
||||
if (self._addonWorker)
|
||||
self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments);
|
||||
});
|
||||
|
||||
// unwrap, recreate and propagate async Errors thrown from content-script
|
||||
this.on("error", function onError({instanceOfError, value}) {
|
||||
if (self._addonWorker) {
|
||||
let error = value;
|
||||
if (instanceOfError) {
|
||||
error = new Error(value.message, value.fileName, value.lineNumber);
|
||||
error.stack = value.stack;
|
||||
error.name = value.name;
|
||||
}
|
||||
self._addonWorker._emit('error', error);
|
||||
}
|
||||
});
|
||||
|
||||
// Inject `addon` global into target document if document is trusted,
|
||||
// `addon` in document is equivalent to `self` in content script.
|
||||
if (worker._injectInDocument) {
|
||||
let win = window.wrappedJSObject ? window.wrappedJSObject : window;
|
||||
Object.defineProperty(win, "addon", {
|
||||
value: content.self,
|
||||
configurable: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Inject our `console` into target document if worker doesn't have a tab
|
||||
// (e.g Panel, PageWorker, Widget).
|
||||
// `worker.tab` can't be used because bug 804935.
|
||||
if (!getTabForContentWindow(window)) {
|
||||
let win = window.wrappedJSObject ? window.wrappedJSObject : window;
|
||||
|
||||
// export our chrome console to content window as described here:
|
||||
// https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
|
||||
let con = Cu.createObjectIn(win);
|
||||
|
||||
let genPropDesc = function genPropDesc(fun) {
|
||||
return { enumerable: true, configurable: true, writable: true,
|
||||
value: console[fun] };
|
||||
}
|
||||
|
||||
const properties = {
|
||||
log: genPropDesc('log'),
|
||||
info: genPropDesc('info'),
|
||||
warn: genPropDesc('warn'),
|
||||
error: genPropDesc('error'),
|
||||
debug: genPropDesc('debug'),
|
||||
trace: genPropDesc('trace'),
|
||||
dir: genPropDesc('dir'),
|
||||
group: genPropDesc('group'),
|
||||
groupCollapsed: genPropDesc('groupCollapsed'),
|
||||
groupEnd: genPropDesc('groupEnd'),
|
||||
time: genPropDesc('time'),
|
||||
timeEnd: genPropDesc('timeEnd'),
|
||||
profile: genPropDesc('profile'),
|
||||
profileEnd: genPropDesc('profileEnd'),
|
||||
exception: genPropDesc('exception'),
|
||||
assert: genPropDesc('assert'),
|
||||
count: genPropDesc('count'),
|
||||
table: genPropDesc('table'),
|
||||
clear: genPropDesc('clear'),
|
||||
dirxml: genPropDesc('dirxml'),
|
||||
markTimeline: genPropDesc('markTimeline'),
|
||||
timeline: genPropDesc('timeline'),
|
||||
timelineEnd: genPropDesc('timelineEnd'),
|
||||
timeStamp: genPropDesc('timeStamp'),
|
||||
};
|
||||
|
||||
Object.defineProperties(con, properties);
|
||||
Cu.makeObjectPropsNormal(con);
|
||||
|
||||
win.console = con;
|
||||
};
|
||||
|
||||
// The order of `contentScriptFile` and `contentScript` evaluation is
|
||||
// intentional, so programs can load libraries like jQuery from script URLs
|
||||
// and use them in scripts.
|
||||
let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile
|
||||
: null,
|
||||
contentScript = ('contentScript' in worker) ? worker.contentScript : null;
|
||||
|
||||
if (contentScriptFile) {
|
||||
if (Array.isArray(contentScriptFile))
|
||||
this._importScripts.apply(this, contentScriptFile);
|
||||
else
|
||||
this._importScripts(contentScriptFile);
|
||||
}
|
||||
if (contentScript) {
|
||||
this._evaluate(
|
||||
Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
|
||||
);
|
||||
}
|
||||
},
|
||||
destroy: function destroy() {
|
||||
this.emitSync("detach");
|
||||
this._sandbox = null;
|
||||
this._addonWorker = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* JavaScript sandbox where all the content scripts are evaluated.
|
||||
* {Sandbox}
|
||||
*/
|
||||
_sandbox: null,
|
||||
|
||||
/**
|
||||
* Reference to the addon side of the worker.
|
||||
* @type {Worker}
|
||||
*/
|
||||
_addonWorker: null,
|
||||
|
||||
/**
|
||||
* Evaluates code in the sandbox.
|
||||
* @param {String} code
|
||||
* JavaScript source to evaluate.
|
||||
* @param {String} [filename='javascript:' + code]
|
||||
* Name of the file
|
||||
*/
|
||||
_evaluate: function(code, filename) {
|
||||
try {
|
||||
evaluate(this._sandbox, code, filename || 'javascript:' + code);
|
||||
}
|
||||
catch(e) {
|
||||
this._addonWorker._emit('error', e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Imports scripts to the sandbox by reading files under urls and
|
||||
* evaluating its source. If exception occurs during evaluation
|
||||
* `"error"` event is emitted on the worker.
|
||||
* This is actually an analog to the `importScript` method in web
|
||||
* workers but in our case it's not exposed even though content
|
||||
* scripts may be able to do it synchronously since IO operation
|
||||
* takes place in the UI process.
|
||||
*/
|
||||
_importScripts: function _importScripts(url) {
|
||||
let urls = Array.slice(arguments, 0);
|
||||
for (let contentScriptFile of urls) {
|
||||
try {
|
||||
let uri = URL(contentScriptFile);
|
||||
if (uri.scheme === 'resource')
|
||||
load(this._sandbox, String(uri));
|
||||
else
|
||||
throw Error("Unsupported `contentScriptFile` url: " + String(uri));
|
||||
}
|
||||
catch(e) {
|
||||
this._addonWorker._emit('error', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Message-passing facility for communication between code running
|
||||
* in the content and add-on process.
|
||||
* @see https://developer.mozilla.org/en-US/Add-ons/SDK/Low-Level_APIs/content_worker
|
||||
*/
|
||||
const Worker = EventEmitter.compose({
|
||||
on: Trait.required,
|
||||
_removeAllListeners: Trait.required,
|
||||
|
||||
// List of messages fired before worker is initialized
|
||||
get _earlyEvents() {
|
||||
delete this._earlyEvents;
|
||||
this._earlyEvents = [];
|
||||
return this._earlyEvents;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a message to the worker's global scope. Method takes single
|
||||
* argument, which represents data to be sent to the worker. The data may
|
||||
* be any primitive type value or `JSON`. Call of this method asynchronously
|
||||
* emits `message` event with data value in the global scope of this
|
||||
* symbiont.
|
||||
*
|
||||
* `message` event listeners can be set either by calling
|
||||
* `self.on` with a first argument string `"message"` or by
|
||||
* implementing `onMessage` function in the global scope of this worker.
|
||||
* @param {Number|String|JSON} data
|
||||
*/
|
||||
postMessage: function (data) {
|
||||
let args = ['message'].concat(Array.slice(arguments));
|
||||
if (!this._inited) {
|
||||
this._earlyEvents.push(args);
|
||||
return;
|
||||
}
|
||||
processMessage.apply(this, args);
|
||||
},
|
||||
|
||||
/**
|
||||
* EventEmitter, that behaves (calls listeners) asynchronously.
|
||||
* A way to send customized messages to / from the worker.
|
||||
* Events from in the worker can be observed / emitted via
|
||||
* worker.on / worker.emit.
|
||||
*/
|
||||
get port() {
|
||||
// We generate dynamically this attribute as it needs to be accessible
|
||||
// before Worker.constructor gets called. (For ex: Panel)
|
||||
|
||||
// create an event emitter that receive and send events from/to the worker
|
||||
this._port = EventEmitterTrait.create({
|
||||
emit: this._emitEventToContent.bind(this)
|
||||
});
|
||||
|
||||
// expose wrapped port, that exposes only public properties:
|
||||
// We need to destroy this getter in order to be able to set the
|
||||
// final value. We need to update only public port attribute as we never
|
||||
// try to access port attribute from private API.
|
||||
delete this._public.port;
|
||||
this._public.port = Cortex(this._port);
|
||||
// Replicate public port to the private object
|
||||
delete this.port;
|
||||
this.port = this._public.port;
|
||||
|
||||
return this._port;
|
||||
},
|
||||
|
||||
/**
|
||||
* Same object than this.port but private API.
|
||||
* Allow access to _emit, in order to send event to port.
|
||||
*/
|
||||
_port: null,
|
||||
|
||||
/**
|
||||
* Emit a custom event to the content script,
|
||||
* i.e. emit this event on `self.port`
|
||||
*/
|
||||
_emitEventToContent: function () {
|
||||
let args = ['event'].concat(Array.slice(arguments));
|
||||
if (!this._inited) {
|
||||
this._earlyEvents.push(args);
|
||||
return;
|
||||
}
|
||||
processMessage.apply(this, args);
|
||||
},
|
||||
|
||||
// Is worker connected to the content worker sandbox ?
|
||||
_inited: false,
|
||||
|
||||
// Is worker being frozen? i.e related document is frozen in bfcache.
|
||||
// Content script should not be reachable if frozen.
|
||||
_frozen: true,
|
||||
|
||||
constructor: function Worker(options) {
|
||||
options = options || {};
|
||||
|
||||
if ('contentScriptFile' in options)
|
||||
this.contentScriptFile = options.contentScriptFile;
|
||||
if ('contentScriptOptions' in options)
|
||||
this.contentScriptOptions = options.contentScriptOptions;
|
||||
if ('contentScript' in options)
|
||||
this.contentScript = options.contentScript;
|
||||
|
||||
this._setListeners(options);
|
||||
|
||||
unload.ensure(this._public, "destroy");
|
||||
|
||||
// Ensure that worker._port is initialized for contentWorker to be able
|
||||
// to send events during worker initialization.
|
||||
this.port;
|
||||
|
||||
this._documentUnload = this._documentUnload.bind(this);
|
||||
this._pageShow = this._pageShow.bind(this);
|
||||
this._pageHide = this._pageHide.bind(this);
|
||||
|
||||
if ("window" in options) this._attach(options.window);
|
||||
},
|
||||
|
||||
_setListeners: function(options) {
|
||||
if ('onError' in options)
|
||||
this.on('error', options.onError);
|
||||
if ('onMessage' in options)
|
||||
this.on('message', options.onMessage);
|
||||
if ('onDetach' in options)
|
||||
this.on('detach', options.onDetach);
|
||||
},
|
||||
|
||||
_attach: function(window) {
|
||||
this._window = window;
|
||||
// Track document unload to destroy this worker.
|
||||
// We can't watch for unload event on page's window object as it
|
||||
// prevents bfcache from working:
|
||||
// https://developer.mozilla.org/En/Working_with_BFCache
|
||||
this._windowID = getInnerId(this._window);
|
||||
observers.on("inner-window-destroyed", this._documentUnload);
|
||||
|
||||
// Listen to pagehide event in order to freeze the content script
|
||||
// while the document is frozen in bfcache:
|
||||
this._window.addEventListener("pageshow", this._pageShow, true);
|
||||
this._window.addEventListener("pagehide", this._pageHide, true);
|
||||
|
||||
// will set this._contentWorker pointing to the private API:
|
||||
this._contentWorker = WorkerSandbox(this);
|
||||
|
||||
// Mainly enable worker.port.emit to send event to the content worker
|
||||
this._inited = true;
|
||||
this._frozen = false;
|
||||
|
||||
// Process all events and messages that were fired before the
|
||||
// worker was initialized.
|
||||
this._earlyEvents.forEach((function (args) {
|
||||
processMessage.apply(this, args);
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
_documentUnload: function _documentUnload({ subject, data }) {
|
||||
let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (innerWinID != this._windowID) return false;
|
||||
this._workerCleanup();
|
||||
return true;
|
||||
},
|
||||
|
||||
_pageShow: function _pageShow() {
|
||||
this._contentWorker.emitSync("pageshow");
|
||||
this._emit("pageshow");
|
||||
this._frozen = false;
|
||||
},
|
||||
|
||||
_pageHide: function _pageHide() {
|
||||
this._contentWorker.emitSync("pagehide");
|
||||
this._emit("pagehide");
|
||||
this._frozen = true;
|
||||
},
|
||||
|
||||
get url() {
|
||||
// this._window will be null after detach
|
||||
return this._window ? this._window.document.location.href : null;
|
||||
},
|
||||
|
||||
get tab() {
|
||||
// this._window will be null after detach
|
||||
if (this._window)
|
||||
return getTabForWindow(this._window);
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tells content worker to unload itself and
|
||||
* removes all the references from itself.
|
||||
*/
|
||||
destroy: function destroy() {
|
||||
this._workerCleanup();
|
||||
this._inited = true;
|
||||
this._removeAllListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all internal references to the attached document
|
||||
* Tells _port to unload itself and removes all the references from itself.
|
||||
*/
|
||||
_workerCleanup: function _workerCleanup() {
|
||||
// maybe unloaded before content side is created
|
||||
// As Symbiont call worker.constructor on document load
|
||||
if (this._contentWorker)
|
||||
this._contentWorker.destroy();
|
||||
this._contentWorker = null;
|
||||
if (this._window) {
|
||||
this._window.removeEventListener("pageshow", this._pageShow, true);
|
||||
this._window.removeEventListener("pagehide", this._pageHide, true);
|
||||
}
|
||||
this._window = null;
|
||||
// This method may be called multiple times,
|
||||
// avoid dispatching `detach` event more than once
|
||||
if (this._windowID) {
|
||||
this._windowID = null;
|
||||
observers.off("inner-window-destroyed", this._documentUnload);
|
||||
this._earlyEvents.length = 0;
|
||||
this._emit("detach");
|
||||
}
|
||||
this._inited = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Receive an event from the content script that need to be sent to
|
||||
* worker.port. Provide a way for composed object to catch all events.
|
||||
*/
|
||||
_onContentScriptEvent: function _onContentScriptEvent() {
|
||||
this._port._emit.apply(this._port, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reference to the content side of the worker.
|
||||
* @type {WorkerGlobalScope}
|
||||
*/
|
||||
_contentWorker: null,
|
||||
|
||||
/**
|
||||
* Reference to the window that is accessible from
|
||||
* the content scripts.
|
||||
* @type {Object}
|
||||
*/
|
||||
_window: null,
|
||||
|
||||
/**
|
||||
* Flag to enable `addon` object injection in document. (bug 612726)
|
||||
* @type {Boolean}
|
||||
*/
|
||||
_injectInDocument: false
|
||||
});
|
||||
|
||||
/**
|
||||
* Fired from postMessage and _emitEventToContent, or from the _earlyMessage
|
||||
* queue when fired before the content is loaded. Sends arguments to
|
||||
* contentWorker if able
|
||||
*/
|
||||
|
||||
function processMessage () {
|
||||
if (!this._contentWorker)
|
||||
throw new Error(ERR_DESTROYED);
|
||||
if (this._frozen)
|
||||
throw new Error(ERR_FROZEN);
|
||||
|
||||
this._contentWorker.emit.apply(null, Array.slice(arguments));
|
||||
}
|
||||
|
||||
exports.Worker = Worker;
|
@ -1,186 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const {
|
||||
compose: _compose,
|
||||
override: _override,
|
||||
resolve: _resolve,
|
||||
trait: _trait,
|
||||
//create: _create,
|
||||
required,
|
||||
} = require('./traits/core');
|
||||
|
||||
const { getOwnPropertyIdentifiers } = require('../util/object');
|
||||
const defineProperties = Object.defineProperties;
|
||||
const freeze = Object.freeze;
|
||||
const create = Object.create;
|
||||
|
||||
/**
|
||||
* Work around bug 608959 by defining the _create function here instead of
|
||||
* importing it from traits/core. For docs on this function, see the create
|
||||
* function in that module.
|
||||
*
|
||||
* FIXME: remove this workaround in favor of importing the function once that
|
||||
* bug has been fixed.
|
||||
*/
|
||||
function _create(proto, trait) {
|
||||
let properties = {},
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (descriptor.required &&
|
||||
!Object.prototype.hasOwnProperty.call(proto, key))
|
||||
throw new Error('Missing required property: ' + key);
|
||||
else if (descriptor.conflict)
|
||||
throw new Error('Remaining conflicting property: ' + key);
|
||||
else
|
||||
properties[key] = descriptor;
|
||||
}
|
||||
return Object.create(proto, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Placeholder for `Trait.prototype`
|
||||
*/
|
||||
let TraitProto = Object.prototype;
|
||||
|
||||
function Get(key) this[key]
|
||||
function Set(key, value) this[key] = value
|
||||
|
||||
/**
|
||||
* Creates anonymous trait descriptor from the passed argument, unless argument
|
||||
* is a trait constructor. In later case trait's already existing properties
|
||||
* descriptor is returned.
|
||||
* This is module's internal function and is used as a gateway to a trait's
|
||||
* internal properties descriptor.
|
||||
* @param {Function} $
|
||||
* Composed trait's constructor.
|
||||
* @returns {Object}
|
||||
* Private trait property of the composition.
|
||||
*/
|
||||
function TraitDescriptor(object)
|
||||
(
|
||||
'function' == typeof object &&
|
||||
(object.prototype == TraitProto || object.prototype instanceof Trait)
|
||||
) ? object._trait(TraitDescriptor) : _trait(object)
|
||||
|
||||
function Public(instance, trait) {
|
||||
let result = {},
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
if (typeof key === 'string' && '_' === key.charAt(0) && '__iterator__' !== key )
|
||||
continue;
|
||||
let property = trait[key],
|
||||
descriptor = {
|
||||
configurable: property.configurable,
|
||||
enumerable: property.enumerable
|
||||
};
|
||||
if (property.get)
|
||||
descriptor.get = property.get.bind(instance);
|
||||
if (property.set)
|
||||
descriptor.set = property.set.bind(instance);
|
||||
if ('value' in property) {
|
||||
let value = property.value;
|
||||
if ('function' === typeof value) {
|
||||
descriptor.value = property.value.bind(instance);
|
||||
descriptor.writable = property.writable;
|
||||
} else {
|
||||
descriptor.get = Get.bind(instance, key);
|
||||
descriptor.set = Set.bind(instance, key);
|
||||
}
|
||||
}
|
||||
result[key] = descriptor;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is private function that composes new trait with privates.
|
||||
*/
|
||||
function Composition(trait) {
|
||||
function Trait() {
|
||||
let self = _create({}, trait);
|
||||
self._public = create(Trait.prototype, Public(self, trait));
|
||||
delete self._public.constructor;
|
||||
if (Object === self.constructor)
|
||||
self.constructor = Trait;
|
||||
else
|
||||
return self.constructor.apply(self, arguments) || self._public;
|
||||
return self._public;
|
||||
}
|
||||
defineProperties(Trait, {
|
||||
prototype: { value: freeze(create(TraitProto, {
|
||||
constructor: { value: constructor, writable: true }
|
||||
}))}, // writable is `true` to avoid getters in custom ES5
|
||||
displayName: { value: (trait.constructor || constructor).name },
|
||||
compose: { value: compose, enumerable: true },
|
||||
override: { value: override, enumerable: true },
|
||||
resolve: { value: resolve, enumerable: true },
|
||||
required: { value: required, enumerable: true },
|
||||
_trait: { value: function _trait(caller)
|
||||
caller === TraitDescriptor ? trait : undefined
|
||||
}
|
||||
});
|
||||
return freeze(Trait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new trait out of itself and traits / property maps passed as an
|
||||
* arguments. If two or more traits / property maps have properties with the
|
||||
* same name, the new trait will contain a "conflict" property for that name.
|
||||
* This is a commutative and associative operation, and the order of its
|
||||
* arguments is not significant.
|
||||
* @params {Object|Function}
|
||||
* List of Traits or property maps to create traits from.
|
||||
* @returns {Function}
|
||||
* New trait containing the combined properties of all the traits.
|
||||
*/
|
||||
function compose() {
|
||||
let traits = Array.slice(arguments, 0);
|
||||
traits.push(this);
|
||||
return Composition(_compose.apply(null, traits.map(TraitDescriptor)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a new trait with all of the combined properties of `this` and the
|
||||
* argument traits. In contrast to `compose`, `override` immediately resolves
|
||||
* all conflicts resulting from this composition by overriding the properties of
|
||||
* later traits. Trait priority is from left to right. I.e. the properties of
|
||||
* the leftmost trait are never overridden.
|
||||
* @params {Object} trait
|
||||
* @returns {Object}
|
||||
*/
|
||||
function override() {
|
||||
let traits = Array.slice(arguments, 0);
|
||||
traits.push(this);
|
||||
return Composition(_override.apply(null, traits.map(TraitDescriptor)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new resolved trait, with all the same properties as this
|
||||
* trait, except that all properties whose name is an own property of
|
||||
* `resolutions` will be renamed to `resolutions[name]`. If it is
|
||||
* `resolutions[name]` is `null` value is changed into a required property
|
||||
* descriptor.
|
||||
*/
|
||||
function resolve(resolutions)
|
||||
Composition(_resolve(resolutions, TraitDescriptor(this)))
|
||||
|
||||
/**
|
||||
* Base Trait, that all the traits are composed of.
|
||||
*/
|
||||
const Trait = Composition({
|
||||
/**
|
||||
* Internal property holding public API of this instance.
|
||||
*/
|
||||
_public: { value: null, configurable: true, writable: true },
|
||||
toString: { value: function() '[object ' + this.constructor.name + ']' }
|
||||
});
|
||||
TraitProto = Trait.prototype;
|
||||
exports.Trait = Trait;
|
@ -1,319 +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";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
// Design inspired by: http://www.traitsjs.org/
|
||||
|
||||
const { getOwnPropertyIdentifiers } = require('../../util/object');
|
||||
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
const _create = Object.create;
|
||||
|
||||
function doPropertiesMatch(object1, object2, name) {
|
||||
// If `object1` has property with the given `name`
|
||||
return name in object1 ?
|
||||
// then `object2` should have it with the same value.
|
||||
name in object2 && object1[name] === object2[name] :
|
||||
// otherwise `object2` should not have property with the given `name`.
|
||||
!(name in object2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two trait custom property descriptors if they are the same. If
|
||||
* both are `conflict` or all the properties of descriptor are equal returned
|
||||
* value will be `true`, otherwise it will be `false`.
|
||||
* @param {Object} desc1
|
||||
* @param {Object} desc2
|
||||
*/
|
||||
function areSame(desc1, desc2) {
|
||||
return ('conflict' in desc1 && desc1.conflict &&
|
||||
'conflict' in desc2 && desc2.conflict) ||
|
||||
(doPropertiesMatch(desc1, desc2, 'get') &&
|
||||
doPropertiesMatch(desc1, desc2, 'set') &&
|
||||
doPropertiesMatch(desc1, desc2, 'value') &&
|
||||
doPropertiesMatch(desc1, desc2, 'enumerable') &&
|
||||
doPropertiesMatch(desc1, desc2, 'required') &&
|
||||
doPropertiesMatch(desc1, desc2, 'conflict'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts array to an object whose own property names represent
|
||||
* values of array.
|
||||
* @param {String[]} names
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* Map(['foo', ...]) => { foo: true, ...}
|
||||
*/
|
||||
function Map(names) {
|
||||
let map = {};
|
||||
for (let name of names)
|
||||
map[name] = true;
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
const ERR_CONFLICT = 'Remaining conflicting property: ',
|
||||
ERR_REQUIRED = 'Missing required property: ';
|
||||
/**
|
||||
* Constant singleton, representing placeholder for required properties.
|
||||
* @type {Object}
|
||||
*/
|
||||
const required = { toString: function()'<Trait.required>' };
|
||||
exports.required = required;
|
||||
|
||||
/**
|
||||
* Generates custom **required** property descriptor. Descriptor contains
|
||||
* non-standard property `required` that is equal to `true`.
|
||||
* @param {String} name
|
||||
* property name to generate descriptor for.
|
||||
* @returns {Object}
|
||||
* custom property descriptor
|
||||
*/
|
||||
function Required(name) {
|
||||
function required() { throw new Error(ERR_REQUIRED + name) }
|
||||
return {
|
||||
get: required,
|
||||
set: required,
|
||||
required: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates custom **conflicting** property descriptor. Descriptor contains
|
||||
* non-standard property `conflict` that is equal to `true`.
|
||||
* @param {String} name
|
||||
* property name to generate descriptor for.
|
||||
* @returns {Object}
|
||||
* custom property descriptor
|
||||
*/
|
||||
function Conflict(name) {
|
||||
function conflict() { throw new Error(ERR_CONFLICT + name) }
|
||||
return {
|
||||
get: conflict,
|
||||
set: conflict,
|
||||
conflict: true
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Function generates custom properties descriptor of the `object`s own
|
||||
* properties. All the inherited properties are going to be ignored.
|
||||
* Properties with values matching `required` singleton will be marked as
|
||||
* 'required' properties.
|
||||
* @param {Object} object
|
||||
* Set of properties to generate trait from.
|
||||
* @returns {Object}
|
||||
* Properties descriptor of all of the `object`'s own properties.
|
||||
*/
|
||||
function trait(properties) {
|
||||
let result = {},
|
||||
keys = getOwnPropertyIdentifiers(properties);
|
||||
for (let key of keys) {
|
||||
let descriptor = getOwnPropertyDescriptor(properties, key);
|
||||
result[key] = (required === descriptor.value) ? Required(key) : descriptor;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.Trait = exports.trait = trait;
|
||||
|
||||
/**
|
||||
* Composes new trait. If two or more traits have own properties with the
|
||||
* same name, the new trait will contain a 'conflict' property for that name.
|
||||
* 'compose' is a commutative and associative operation, and the order of its
|
||||
* arguments is not significant.
|
||||
*
|
||||
* @params {Object} trait
|
||||
* Takes traits as an arguments
|
||||
* @returns {Object}
|
||||
* New trait containing the combined own properties of all the traits.
|
||||
* @example
|
||||
* var newTrait = compose(trait_1, trait_2, ..., trait_N);
|
||||
*/
|
||||
function compose(trait1, trait2) {
|
||||
let traits = Array.slice(arguments, 0),
|
||||
result = {};
|
||||
for (let trait of traits) {
|
||||
let keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
// if property already exists and it's not a requirement
|
||||
if (hasOwn.call(result, key) && !result[key].required) {
|
||||
if (descriptor.required)
|
||||
continue;
|
||||
if (!areSame(descriptor, result[key]))
|
||||
result[key] = Conflict(key);
|
||||
} else {
|
||||
result[key] = descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.compose = compose;
|
||||
|
||||
/**
|
||||
* Composes new trait with the same own properties as the original trait,
|
||||
* except that all property names appearing in the first argument are replaced
|
||||
* by 'required' property descriptors.
|
||||
* @param {String[]} keys
|
||||
* Array of strings property names.
|
||||
* @param {Object} trait
|
||||
* A trait some properties of which should be excluded.
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* var newTrait = exclude(['name', ...], trait)
|
||||
*/
|
||||
function exclude(keys, trait) {
|
||||
let exclusions = Map(keys),
|
||||
result = {};
|
||||
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
|
||||
for (let key of keys) {
|
||||
if (!hasOwn.call(exclusions, key) || trait[key].required)
|
||||
result[key] = trait[key];
|
||||
else
|
||||
result[key] = Required(key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a new trait with all of the combined properties of the argument
|
||||
* traits. In contrast to `compose`, `override` immediately resolves all
|
||||
* conflicts resulting from this composition by overriding the properties of
|
||||
* later traits. Trait priority is from left to right. I.e. the properties of
|
||||
* the leftmost trait are never overridden.
|
||||
* @params {Object} trait
|
||||
* @returns {Object}
|
||||
* @examples
|
||||
* // override is associative:
|
||||
* override(t1,t2,t3)
|
||||
* // is equivalent to
|
||||
* override(t1, override(t2, t3))
|
||||
* // or
|
||||
* to override(override(t1, t2), t3)
|
||||
*
|
||||
* // override is not commutative:
|
||||
* override(t1,t2)
|
||||
* // is not equivalent to
|
||||
* override(t2,t1)
|
||||
*/
|
||||
function override() {
|
||||
let traits = Array.slice(arguments, 0),
|
||||
result = {};
|
||||
for (let trait of traits) {
|
||||
let keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (!hasOwn.call(result, key) || result[key].required)
|
||||
result[key] = descriptor;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
exports.override = override;
|
||||
|
||||
/**
|
||||
* Composes a new trait with the same properties as the original trait, except
|
||||
* that all properties whose name is an own property of map will be renamed to
|
||||
* map[name], and a 'required' property for name will be added instead.
|
||||
* @param {Object} map
|
||||
* An object whose own properties serve as a mapping from old names to new
|
||||
* names.
|
||||
* @param {Object} trait
|
||||
* A trait object
|
||||
* @returns {Object}
|
||||
* @example
|
||||
* var newTrait = rename(map, trait);
|
||||
*/
|
||||
function rename(map, trait) {
|
||||
let result = {},
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
// must be renamed & it's not requirement
|
||||
if (hasOwn.call(map, key) && !trait[key].required) {
|
||||
let alias = map[key];
|
||||
if (hasOwn.call(result, alias) && !result[alias].required)
|
||||
result[alias] = Conflict(alias);
|
||||
else
|
||||
result[alias] = trait[key];
|
||||
if (!hasOwn.call(result, key))
|
||||
result[key] = Required(key);
|
||||
} else { // must not be renamed or its a requirement
|
||||
// property is not in result trait yet
|
||||
if (!hasOwn.call(result, key))
|
||||
result[key] = trait[key];
|
||||
// property is already in resulted trait & it's not requirement
|
||||
else if (!trait[key].required)
|
||||
result[key] = Conflict(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes new resolved trait, with all the same properties as the original
|
||||
* trait, except that all properties whose name is an own property of
|
||||
* resolutions will be renamed to `resolutions[name]`. If it is
|
||||
* `resolutions[name]` is `null` value is changed into a required property
|
||||
* descriptor.
|
||||
* function can be implemented as `rename(map,exclude(exclusions, trait))`
|
||||
* where map is the subset of mappings from oldName to newName and exclusions
|
||||
* is an array of all the keys that map to `null`.
|
||||
* Note: it's important to **first** `exclude`, **then** `rename`, since
|
||||
* `exclude` and rename are not associative.
|
||||
* @param {Object} resolutions
|
||||
* An object whose own properties serve as a mapping from old names to new
|
||||
* names, or to `null` if the property should be excluded.
|
||||
* @param {Object} trait
|
||||
* A trait object
|
||||
* @returns {Object}
|
||||
* Resolved trait with the same own properties as the original trait.
|
||||
*/
|
||||
function resolve(resolutions, trait) {
|
||||
let renames = {},
|
||||
exclusions = [],
|
||||
keys = getOwnPropertyIdentifiers(resolutions);
|
||||
for (let key of keys) { // pre-process renamed and excluded properties
|
||||
if (resolutions[key]) // old name -> new name
|
||||
renames[key] = resolutions[key];
|
||||
else // name -> undefined
|
||||
exclusions.push(key);
|
||||
}
|
||||
return rename(renames, exclude(exclusions, trait));
|
||||
}
|
||||
exports.resolve = resolve;
|
||||
|
||||
/**
|
||||
* `create` is like `Object.create`, except that it ensures that:
|
||||
* - an exception is thrown if 'trait' still contains required properties
|
||||
* - an exception is thrown if 'trait' still contains conflicting
|
||||
* properties
|
||||
* @param {Object}
|
||||
* prototype of the completed object
|
||||
* @param {Object} trait
|
||||
* trait object to be turned into a complete object
|
||||
* @returns {Object}
|
||||
* An object with all of the properties described by the trait.
|
||||
*/
|
||||
function create(proto, trait) {
|
||||
let properties = {},
|
||||
keys = getOwnPropertyIdentifiers(trait);
|
||||
for (let key of keys) {
|
||||
let descriptor = trait[key];
|
||||
if (descriptor.required && !hasOwn.call(proto, key))
|
||||
throw new Error(ERR_REQUIRED + key);
|
||||
else if (descriptor.conflict)
|
||||
throw new Error(ERR_CONFLICT + key);
|
||||
else
|
||||
properties[key] = descriptor;
|
||||
}
|
||||
return _create(proto, properties);
|
||||
}
|
||||
exports.create = create;
|
@ -14,7 +14,7 @@ const { getTabs, closeTab, getURI, getTabId, getSelectedTab } = require("../tabs
|
||||
const { windows, isBrowser, getMostRecentBrowserWindow } = require("../window/utils");
|
||||
const { defer, all, Debugging: PromiseDebugging, resolve } = require("../core/promise");
|
||||
const { getInnerId } = require("../window/utils");
|
||||
const { cleanUI } = require("../test/utils")
|
||||
const { cleanUI } = require("../test/utils");
|
||||
|
||||
const findAndRunTests = function findAndRunTests(options) {
|
||||
var TestFinder = require("./unit-test-finder").TestFinder;
|
||||
|
@ -8,13 +8,9 @@ module.metadata = {
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require('chrome');
|
||||
const { EventEmitter } = require('../deprecated/events');
|
||||
const { Trait } = require('../deprecated/traits');
|
||||
const { when } = require('../system/unload');
|
||||
const events = require('../system/events');
|
||||
const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser,
|
||||
getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils');
|
||||
const errors = require('../deprecated/errors');
|
||||
const { deprecateFunction } = require('../util/deprecate');
|
||||
const { ignoreWindow } = require('sdk/private-browsing/utils');
|
||||
const { isPrivateBrowsingSupported } = require('../self');
|
||||
@ -124,13 +120,18 @@ WindowTracker.prototype = {
|
||||
this._unregWindow(window);
|
||||
},
|
||||
|
||||
handleEvent: errors.catchAndLog(function handleEvent(event) {
|
||||
handleEvent: function handleEvent(event) {
|
||||
try {
|
||||
if (event.type == 'load' && event.target) {
|
||||
var window = event.target.defaultView;
|
||||
if (window)
|
||||
this._regWindow(window);
|
||||
}
|
||||
}),
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
},
|
||||
|
||||
_onToplevelWindowReady: function _onToplevelWindowReady({subject}) {
|
||||
let window = subject;
|
||||
@ -140,47 +141,22 @@ WindowTracker.prototype = {
|
||||
this._regWindow(window);
|
||||
},
|
||||
|
||||
observe: errors.catchAndLog(function observe(subject, topic, data) {
|
||||
observe: function observe(subject, topic, data) {
|
||||
try {
|
||||
var window = subject.QueryInterface(Ci.nsIDOMWindow);
|
||||
// ignore private windows if they are not supported
|
||||
if (ignoreWindow(window))
|
||||
return;
|
||||
if (topic == 'domwindowclosed')
|
||||
this._unregWindow(window);
|
||||
})
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.WindowTracker = WindowTracker;
|
||||
|
||||
const WindowTrackerTrait = Trait.compose({
|
||||
_onTrack: Trait.required,
|
||||
_onUntrack: Trait.required,
|
||||
constructor: function WindowTrackerTrait() {
|
||||
WindowTracker({
|
||||
onTrack: this._onTrack.bind(this),
|
||||
onUntrack: this._onUntrack.bind(this)
|
||||
});
|
||||
}
|
||||
});
|
||||
exports.WindowTrackerTrait = WindowTrackerTrait;
|
||||
|
||||
var gDocsToClose = [];
|
||||
|
||||
function onDocUnload(event) {
|
||||
var index = gDocsToClose.indexOf(event.target);
|
||||
if (index == -1)
|
||||
throw new Error('internal error: unloading document not found');
|
||||
var document = gDocsToClose.splice(index, 1)[0];
|
||||
// Just in case, let's remove the event listener too.
|
||||
document.defaultView.removeEventListener('unload', onDocUnload, false);
|
||||
}
|
||||
|
||||
onDocUnload = require('./errors').catchAndLog(onDocUnload);
|
||||
|
||||
exports.closeOnUnload = function closeOnUnload(window) {
|
||||
window.addEventListener('unload', onDocUnload, false);
|
||||
gDocsToClose.push(window.document);
|
||||
};
|
||||
|
||||
Object.defineProperties(exports, {
|
||||
activeWindow: {
|
||||
enumerable: true,
|
||||
@ -219,9 +195,3 @@ exports.isBrowser = deprecateFunction(isBrowser,
|
||||
);
|
||||
|
||||
exports.hiddenWindow = appShellService.hiddenDOMWindow;
|
||||
|
||||
when(
|
||||
function() {
|
||||
gDocsToClose.slice().forEach(
|
||||
function(doc) { doc.defaultView.close(); });
|
||||
});
|
||||
|
@ -86,6 +86,14 @@ exports.once = once;
|
||||
* Arguments that will be passed to listeners.
|
||||
*/
|
||||
function emit (target, type, ...args) {
|
||||
emitOnObject(target, type, target, ...args);
|
||||
}
|
||||
exports.emit = emit;
|
||||
|
||||
/**
|
||||
* A variant of emit that allows setting the this property for event listeners
|
||||
*/
|
||||
function emitOnObject(target, type, thisArg, ...args) {
|
||||
let all = observers(target, '*').length;
|
||||
let state = observers(target, type);
|
||||
let listeners = state.slice();
|
||||
@ -101,7 +109,7 @@ function emit (target, type, ...args) {
|
||||
let listener = listeners[index];
|
||||
// Dispatch only if listener is still registered.
|
||||
if (~state.indexOf(listener))
|
||||
listener.apply(target, args);
|
||||
listener.apply(thisArg, args);
|
||||
}
|
||||
catch (error) {
|
||||
// If exception is not thrown by a error listener and error listener is
|
||||
@ -114,7 +122,7 @@ function emit (target, type, ...args) {
|
||||
// Also emit on `"*"` so that one could listen for all events.
|
||||
if (type !== '*') emit(target, '*', type, ...args);
|
||||
}
|
||||
exports.emit = emit;
|
||||
exports.emitOnObject = emitOnObject;
|
||||
|
||||
/**
|
||||
* Removes an event `listener` for the given event `type` on the given event
|
||||
|
@ -9,7 +9,6 @@ module.metadata = {
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const errors = require("../deprecated/errors");
|
||||
const { Class } = require("../core/heritage");
|
||||
const { List, addListItem, removeListItem } = require("../util/list");
|
||||
const { EventTarget } = require("../event/target");
|
||||
|
@ -1,80 +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 { windows, isInteractive, isDocumentLoaded,
|
||||
getOuterId, isTopLevel } = require("../window/utils");
|
||||
const { InputPort } = require("./system");
|
||||
const { lift, merges, foldp, keepIf, start, Input } = require("../event/utils");
|
||||
const { patch } = require("diffpatcher/index");
|
||||
const { on } = require("../event/core");
|
||||
const { Sequence, seq, filter, object, pairs } = require("../util/sequence");
|
||||
|
||||
|
||||
// Create lazy iterators from the regular arrays, although
|
||||
// once https://github.com/mozilla/addon-sdk/pull/1314 lands
|
||||
// `windows` will be transforme to lazy iterators.
|
||||
// When iterated over belowe sequences items will represent
|
||||
// state of windows at the time of iteration.
|
||||
const opened = seq(function*() {
|
||||
const items = windows(null, {includePrivates: true});
|
||||
for (let item of items)
|
||||
yield [getOuterId(item), item];
|
||||
});
|
||||
const interactive = filter(([_, window]) => isInteractive(window), opened);
|
||||
const loaded = filter(([_, window]) => isDocumentLoaded(window), opened);
|
||||
|
||||
// Helper function that converts given argument to a delta.
|
||||
const Update = window => window && object([getOuterId(window), window]);
|
||||
const Delete = window => window && object([getOuterId(window), null]);
|
||||
|
||||
// Signal represents delta for last opened top level window.
|
||||
const LastOpened = lift(Update, new InputPort({topic: "domwindowopened"}));
|
||||
exports.LastOpened = LastOpened;
|
||||
|
||||
// Signal represents delta for last top level window close.
|
||||
const LastClosed = lift(Delete, new InputPort({topic: "domwindowclosed"}));
|
||||
exports.LastClosed = LastClosed;
|
||||
|
||||
const windowFor = document => document && document.defaultView;
|
||||
|
||||
// Signal represent delta for last top level window document becoming interactive.
|
||||
const InteractiveDoc = new InputPort({topic: "chrome-document-interactive"});
|
||||
const InteractiveWin = lift(windowFor, InteractiveDoc);
|
||||
const LastInteractive = lift(Update, keepIf(isTopLevel, null, InteractiveWin));
|
||||
exports.LastInteractive = LastInteractive;
|
||||
|
||||
// Signal represent delta for last top level window loaded.
|
||||
const LoadedDoc = new InputPort({topic: "chrome-document-loaded"});
|
||||
const LoadedWin = lift(windowFor, LoadedDoc);
|
||||
const LastLoaded = lift(Update, keepIf(isTopLevel, null, LoadedWin));
|
||||
exports.LastLoaded = LastLoaded;
|
||||
|
||||
|
||||
const initialize = input => {
|
||||
if (!input.initialized) {
|
||||
input.value = object(...input.value);
|
||||
Input.start(input);
|
||||
input.initialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Signal represents set of currently opened top level windows, updated
|
||||
// to new set any time window is opened or closed.
|
||||
const Opened = foldp(patch, opened, merges([LastOpened, LastClosed]));
|
||||
Opened[start] = initialize;
|
||||
exports.Opened = Opened;
|
||||
|
||||
// Signal represents set of top level interactive windows, updated any
|
||||
// time new window becomes interactive or one get's closed.
|
||||
const Interactive = foldp(patch, interactive, merges([LastInteractive,
|
||||
LastClosed]));
|
||||
Interactive[start] = initialize;
|
||||
exports.Interactive = Interactive;
|
||||
|
||||
// Signal represents set of top level loaded window, updated any time
|
||||
// new window becomes interactive or one get's closed.
|
||||
const Loaded = foldp(patch, loaded, merges([LastLoaded, LastClosed]));
|
||||
Loaded[start] = initialize;
|
||||
exports.Loaded = Loaded;
|
@ -5,7 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
"stability": "experimental"
|
||||
"stability": "deprecated"
|
||||
};
|
||||
|
||||
const {Cc,Ci,Cr} = require("chrome");
|
||||
|
@ -884,6 +884,7 @@ exports.writeFile = writeFile;
|
||||
* The synchronous version of `fs.writeFile`.
|
||||
*/
|
||||
function writeFileSync(filename, data, encoding) {
|
||||
// TODO: Implement this in bug 1148209 https://bugzilla.mozilla.org/show_bug.cgi?id=1148209
|
||||
throw Error("Not implemented");
|
||||
};
|
||||
exports.writeFileSync = writeFileSync;
|
||||
|
@ -32,7 +32,7 @@ const InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1",
|
||||
const threadManager = Cc["@mozilla.org/thread-manager;1"].
|
||||
getService(Ci.nsIThreadManager);
|
||||
|
||||
const eventTarget = Cc["@mozilla.org/network/socket-transport-service;1"].
|
||||
const eventTarget = Cc["@mozilla.org/network/stream-transport-service;1"].
|
||||
getService(Ci.nsIEventTarget);
|
||||
|
||||
let isFunction = value => typeof(value) === "function"
|
||||
|
@ -125,9 +125,7 @@ exports.isGenerator = isGenerator;
|
||||
* isArray([1, 2, 3]) // true
|
||||
* isArray({ 0: 'foo', length: 1 }) // false
|
||||
*/
|
||||
var isArray = Array.isArray || function isArray(value) {
|
||||
Object.prototype.toString.call(value) === "[object Array]";
|
||||
}
|
||||
var isArray = Array.isArray;
|
||||
exports.isArray = isArray;
|
||||
|
||||
/**
|
||||
@ -137,7 +135,7 @@ exports.isArray = isArray;
|
||||
* isArguments([1,2,3]); // false
|
||||
*/
|
||||
function isArguments(value) {
|
||||
Object.prototype.toString.call(value) === "[object Arguments]";
|
||||
return Object.prototype.toString.call(value) === "[object Arguments]";
|
||||
}
|
||||
exports.isArguments = isArguments;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* 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/.
|
||||
*/
|
||||
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
|
@ -10,7 +10,6 @@ module.metadata = {
|
||||
|
||||
const { Cc, Ci, Cr } = require("chrome");
|
||||
const apiUtils = require("./deprecated/api-utils");
|
||||
const errors = require("./deprecated/errors");
|
||||
const { isString, isUndefined, instanceOf } = require('./lang/type');
|
||||
const { URL, isLocalURL } = require('./url');
|
||||
const { data } = require('./self');
|
||||
@ -34,9 +33,15 @@ catch (err) {
|
||||
exports.notify = function notifications_notify(options) {
|
||||
let valOpts = validateOptions(options);
|
||||
let clickObserver = !valOpts.onClick ? null : {
|
||||
observe: function notificationClickObserved(subject, topic, data) {
|
||||
if (topic === "alertclickcallback")
|
||||
errors.catchAndLog(valOpts.onClick).call(exports, valOpts.data);
|
||||
observe: (subject, topic, data) => {
|
||||
if (topic === "alertclickcallback") {
|
||||
try {
|
||||
valOpts.onClick.call(exports, valOpts.data);
|
||||
}
|
||||
catch(e) {
|
||||
console.exception(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
function notifyWithOpts(notifyFn) {
|
||||
|
@ -125,9 +125,12 @@ const PageMod = Class({
|
||||
}
|
||||
}
|
||||
|
||||
model.childOptions = omit(model, ["include", "exclude"]);
|
||||
model.childOptions = omit(model, ["include", "exclude", "contentScriptOptions"]);
|
||||
model.childOptions.include = [...serializeRules(model.include)];
|
||||
model.childOptions.exclude = [...serializeRules(model.exclude)];
|
||||
model.childOptions.contentScriptOptions = model.contentScriptOptions ?
|
||||
JSON.stringify(model.contentScriptOptions) :
|
||||
null;
|
||||
|
||||
processes.port.emit('sdk/page-mod/create', model.childOptions);
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ const { has } = require('./util/array');
|
||||
const { Rules } = require('./util/rules');
|
||||
const { merge } = require('./util/object');
|
||||
const { data } = require('./self');
|
||||
const { getActiveView } = require("./view/core");
|
||||
|
||||
const views = new WeakMap();
|
||||
const workers = new WeakMap();
|
||||
@ -181,3 +182,5 @@ function pageFromDoc(doc) {
|
||||
return page;
|
||||
return null;
|
||||
}
|
||||
|
||||
getActiveView.define(Page, viewFor);
|
||||
|
@ -1,7 +1,6 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.metadata = {
|
||||
@ -9,7 +8,6 @@ module.metadata = {
|
||||
};
|
||||
|
||||
const { Cc, Ci } = require('chrome');
|
||||
const { EventEmitter } = require('../deprecated/events');
|
||||
const { isValidURI, URL } = require('../url');
|
||||
const { contract } = require('../util/contract');
|
||||
const { extend } = require('../util/object');
|
||||
|
@ -46,7 +46,7 @@ let pathMapping = Object.keys(options.paths)
|
||||
let { getNewLoaderID } = require('../../framescript/FrameScriptManager.jsm');
|
||||
let PATH = options.paths[''];
|
||||
|
||||
const childOptions = omit(options, ['modules', 'globals']);
|
||||
const childOptions = omit(options, ['modules', 'globals', 'resolve', 'load']);
|
||||
childOptions.modules = {};
|
||||
// @l10n/data is just JSON data and can be safely sent across to the child loader
|
||||
try {
|
||||
|
@ -164,6 +164,8 @@ const Response = Class({
|
||||
initialize: function initialize(request) {
|
||||
response(this).request = request;
|
||||
},
|
||||
// more about responseURL: https://bugzilla.mozilla.org/show_bug.cgi?id=998076
|
||||
get url() response(this).request.responseURL,
|
||||
get text() response(this).request.responseText,
|
||||
get xml() {
|
||||
throw new Error("Sorry, the 'xml' property is no longer available. " +
|
||||
|
@ -9,7 +9,6 @@ module.metadata = {
|
||||
|
||||
const { Cc, Ci, CC } = require('chrome');
|
||||
const options = require('@loader/options');
|
||||
const file = require('./io/file');
|
||||
const runtime = require("./system/runtime");
|
||||
const { when: unload } = require("./system/unload");
|
||||
|
||||
|
@ -7,11 +7,6 @@ module.metadata = {
|
||||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { modelFor } = require("./model/core");
|
||||
const { viewFor } = require("./view/core");
|
||||
const { isTab } = require("./tabs/utils");
|
||||
|
||||
|
||||
if (require("./system/xul-app").is("Fennec")) {
|
||||
module.exports = require("./windows/tabs-fennec").tabs;
|
||||
}
|
||||
@ -20,14 +15,3 @@ else {
|
||||
}
|
||||
|
||||
const tabs = module.exports;
|
||||
|
||||
// Implement `modelFor` function for the Tab instances.
|
||||
// Finds a right model by iterating over all tab models
|
||||
// and finding one that wraps given `view`.
|
||||
modelFor.when(isTab, view => {
|
||||
for (let model of tabs) {
|
||||
if (viewFor(model) === view)
|
||||
return model;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|