зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central
This commit is contained in:
Коммит
c78cc390fa
|
@ -35,13 +35,13 @@ geolocation API in Firefox.
|
|||
## Using Geolocation in an Add-on ##
|
||||
|
||||
Suppose we want to use the
|
||||
[geolocation API built into Firefox](https://developer.mozilla.org/en/using_geolocation).
|
||||
[geolocation API built into Firefox](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation).
|
||||
The SDK doesn't provide an API to access geolocation, but we can
|
||||
[access the underlying XPCOM API using `require("chrome")`](dev-guide/guides/xul-migration.html#xpcom).
|
||||
|
||||
The following add-on adds a [button to the toolbar](dev-guide/tutorials/adding-toolbar-button.html):
|
||||
when the user clicks the button, it loads the
|
||||
[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
|
||||
[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
|
||||
object, and retrieves the user's current position:
|
||||
|
||||
var {Cc, Ci} = require("chrome");
|
||||
|
@ -88,7 +88,7 @@ info: longitude: 93.0785269
|
|||
</pre>
|
||||
|
||||
So far, so good. But the geolocation guide on MDN tells us that we must
|
||||
[ask the user for permission](https://developer.mozilla.org/en/using_geolocation#Prompting_for_permission)
|
||||
[ask the user for permission](https://developer.mozilla.org/en-US/docs/WebAPI/Using_geolocation#Prompting_for_permission)
|
||||
before using the API.
|
||||
|
||||
So we'll extend the add-on to include an adapted version of the code in
|
||||
|
|
|
@ -13,15 +13,17 @@ When the method is invoked on an instance of the object, the original
|
|||
function is called. It is passed the object instance (i.e. `this`) as
|
||||
the first parameter, followed by any parameters passed into the method.
|
||||
|
||||
let { method } = require("sdk/lang/functional");
|
||||
let myNumber = {
|
||||
const { method } = require("sdk/lang/functional");
|
||||
|
||||
const times = (target, x) => target.number *= x;
|
||||
const add = (target, x) => target.number += x;
|
||||
|
||||
const myNumber = {
|
||||
times: method(times),
|
||||
add: method(add),
|
||||
number: 0
|
||||
};
|
||||
|
||||
function times (target, x) { return target.number *= x; }
|
||||
function add (target, x) { return target.number += x; }
|
||||
|
||||
console.log(myNumber.number); // 0
|
||||
myNumber.add(10); // 10
|
||||
|
@ -44,8 +46,8 @@ wait (i.e. `setTimeout(function () { ... }, 0)`), except that the wrapped functi
|
|||
may be reused and does not need to be repeated each time. This also enables you
|
||||
to use these functions as event listeners.
|
||||
|
||||
let { defer } = require("sdk/lang/functional");
|
||||
let fn = defer(function myEvent (event, value) {
|
||||
const { defer } = require("sdk/lang/functional");
|
||||
const fn = defer((event, value) => {
|
||||
console.log(event + " : " + value);
|
||||
});
|
||||
|
||||
|
@ -74,16 +76,11 @@ An alias for [defer](modules/sdk/lang/functional.html#defer(fn)).
|
|||
Invokes `callee`, passing `params` as an argument and `self` as `this`.
|
||||
Returns the value that is returned by `callee`.
|
||||
|
||||
let { invoke } = require("sdk/lang/functional");
|
||||
const { invoke } = require("sdk/lang/functional");
|
||||
|
||||
const sum = (...args) => args.reduce((a, b) => a + b);
|
||||
invoke(sum, [1,2,3,4,5], null); // 15
|
||||
|
||||
function sum () {
|
||||
return Array.slice(arguments).reduce(function (a, b) {
|
||||
return a + b;
|
||||
});
|
||||
}
|
||||
|
||||
@param callee {function}
|
||||
Function to invoke.
|
||||
@param params {Array}
|
||||
|
@ -98,9 +95,9 @@ Returns the value that is returned by `callee`.
|
|||
@function
|
||||
Takes a function and bind values to one or more arguments, returning a new function of smaller arity.
|
||||
|
||||
let { partial } = require("sdk/lang/functional");
|
||||
let add = function add (x, y) { return x + y; }
|
||||
let addOne = partial(add, 1);
|
||||
const { partial } = require("sdk/lang/functional");
|
||||
const add = (x, y) => x + y;
|
||||
const addOne = partial(add, 1);
|
||||
|
||||
addOne(5); // 6
|
||||
addOne(10); // 11
|
||||
|
@ -122,14 +119,16 @@ Returns the [composition](http://en.wikipedia.org/wiki/Function_composition_(com
|
|||
return value of the function that follows. In math terms, composing the functions
|
||||
`f()`, `g()`, and `h()` produces `f(g(h()))`.
|
||||
|
||||
let { compose } = require("sdk/lang/functional");
|
||||
const { compose } = require("sdk/lang/functional");
|
||||
|
||||
let welcome = compose(exclaim, greet);
|
||||
const square = x => x * x;
|
||||
const increment = x => x + 1;
|
||||
|
||||
welcome('moe'); // "hi: moe!";
|
||||
const f1 = compose(increment, square);
|
||||
f1(5); // => 26
|
||||
|
||||
function greet (name) { return "hi: " + name; }
|
||||
function exclaim (statement) { return statement + "!"; }
|
||||
const f2 = compose(square, increment);
|
||||
f2(5); // => 36
|
||||
|
||||
@param fn... {function}
|
||||
Takes a variable number of functions as arguments and composes them from right to left.
|
||||
|
@ -144,16 +143,14 @@ Returns the first function passed as an argument to the second,
|
|||
allowing you to adjust arguments, run code before and after, and
|
||||
conditionally execute the original function.
|
||||
|
||||
let { wrap } = require("sdk/lang/functional");
|
||||
const { wrap } = require("sdk/lang/functional");
|
||||
|
||||
let wrappedHello = wrap(hello, function (fn, name) {
|
||||
return "before, " + fn(name) + "after";
|
||||
});
|
||||
const hello = name => "hello: " + name;
|
||||
const wrappedHello = wrap(hello, (fn, name) =>
|
||||
"before, " + fn(name) + "after");
|
||||
|
||||
wrappedHello("moe"); // "before, hello: moe, after"
|
||||
|
||||
function hello (name) { return "hello: " + name; }
|
||||
|
||||
@param fn {function}
|
||||
The function to be passed into the `wrapper` function.
|
||||
|
||||
|
@ -170,8 +167,8 @@ conditionally execute the original function.
|
|||
@function
|
||||
Returns the same value that is used as the argument. In math: f(x) = x.
|
||||
|
||||
let { identity } = require("sdk/lang/functional");
|
||||
let x = 5;
|
||||
const { identity } = require("sdk/lang/functional");
|
||||
const x = 5;
|
||||
identity(x); // 5
|
||||
|
||||
@param value {mixed}
|
||||
|
@ -190,15 +187,15 @@ storing the result, based on the arguments to the original function. The
|
|||
default `hashFunction` just uses the first argument to the memoized function as
|
||||
the key.
|
||||
|
||||
let { memoize } = require("sdk/lang/functional");
|
||||
const { memoize } = require("sdk/lang/functional");
|
||||
|
||||
let memoizedFn = memoize(primeFactorization);
|
||||
const memoizedFn = memoize(primeFactorization);
|
||||
|
||||
memoizedFn(50); // Returns [2, 5, 5], had to compute
|
||||
memoizedFn(100); // Returns [2, 2, 5, 5], had to compute
|
||||
memoizedFn(50); // Returns [2, 5, 5] again, but pulled from cache
|
||||
|
||||
function primeFactorization (x) {
|
||||
const primeFactorization = x => {
|
||||
// Some tricky stuff
|
||||
}
|
||||
|
||||
|
@ -209,16 +206,14 @@ the key.
|
|||
// function will just parse the last name, as our naive
|
||||
// implementation assumes that they will share the same lineage
|
||||
|
||||
let getLineage = memoize(function (name) {
|
||||
const getLineage = memoize(name => {
|
||||
// computes lineage
|
||||
return data;
|
||||
}, hasher);
|
||||
|
||||
// Hashing function takes a string of first and last name
|
||||
// and returns the last name.
|
||||
function hasher (input) {
|
||||
return input.split(" ")[1];
|
||||
}
|
||||
const hasher = input => input.split(" ")[1];
|
||||
|
||||
getLineage("homer simpson"); // Computes and returns information for "simpson"
|
||||
getLineage("lisa simpson"); // Returns cached for "simpson"
|
||||
|
@ -240,12 +235,11 @@ Much like `setTimeout`, `delay` invokes a function after waiting a set number of
|
|||
milliseconds. If you pass additional, optional, arguments, they will be forwarded
|
||||
on to the function when it is invoked.
|
||||
|
||||
let { delay } = require("sdk/lang/functional");
|
||||
const { delay } = require("sdk/lang/functional");
|
||||
|
||||
const printAdd = (a, b) console.log(a + "+" + b + "=" + (a+b));
|
||||
delay(printAdd, 2000, 5, 10);
|
||||
|
||||
// Prints "5+10=15" in two seconds (2000ms)
|
||||
function printAdd (a, b) { console.log(a + "+" + b + "=" + (a+b)); }
|
||||
|
||||
@param fn {function}
|
||||
A function to be delayed.
|
||||
|
@ -264,8 +258,8 @@ Repeated calls to the modified function will have no effect, returning
|
|||
the value from the original call. Useful for initialization functions, instead
|
||||
of having to set a boolean flag and checking it later.
|
||||
|
||||
let { once } = require("sdk/lang/functional");
|
||||
let setup = once(function (env) {
|
||||
const { once } = require("sdk/lang/functional");
|
||||
const setup = once(env => {
|
||||
// initializing important things
|
||||
console.log("successfully initialized " + env);
|
||||
return 1; // Assume success and return 1
|
||||
|
@ -286,16 +280,156 @@ of having to set a boolean flag and checking it later.
|
|||
The wrapped `fn` that can only be executed once.
|
||||
</api>
|
||||
|
||||
<api name="chain">
|
||||
<api name="cache">
|
||||
@function
|
||||
An alias for [once](modules/sdk/lang/functional.html#once(fn)).
|
||||
</api>
|
||||
|
||||
<api name="complement">
|
||||
@function
|
||||
Takes a `f` function and returns a function that takes the same
|
||||
arguments as `f`, has the same effects, if any, and returns the
|
||||
opposite truth value.
|
||||
|
||||
const { complement } = require("sdk/lang/functional");
|
||||
|
||||
let isOdd = x => Boolean(x % 2);
|
||||
|
||||
isOdd(1) // => true
|
||||
isOdd(2) // => false
|
||||
|
||||
let isEven = complement(isOdd);
|
||||
|
||||
isEven(1) // => false
|
||||
isEven(2) // => true
|
||||
|
||||
@param lambda {function}
|
||||
The function to compose from
|
||||
|
||||
@returns {boolean}
|
||||
`!lambda(...)`
|
||||
</api>
|
||||
|
||||
<api name="constant">
|
||||
@function
|
||||
Constructs function that returns `x` no matter what is it
|
||||
invoked with.
|
||||
|
||||
const { constant } = require("sdk/lang/functional");
|
||||
|
||||
const one = constant(1);
|
||||
|
||||
one(); // => 1
|
||||
one(2); // => 1
|
||||
one.apply({}, 3); // => 1
|
||||
|
||||
@param x {object}
|
||||
Value that will be returned by composed function
|
||||
|
||||
@returns {function}
|
||||
</api>
|
||||
|
||||
|
||||
<api name="apply">
|
||||
@function
|
||||
Apply function that behaves like `apply` in other functional
|
||||
languages:
|
||||
|
||||
const { apply } = require("sdk/lang/functional");
|
||||
|
||||
const dashify = (...args) => args.join("-");
|
||||
|
||||
apply(dashify, 1, [2, 3]); // => "1-2-3"
|
||||
apply(dashify, "a"); // => "a"
|
||||
apply(dashify, ["a", "b"]); // => "a-b"
|
||||
apply(dashify, ["a", "b"], "c"); // => "a,b-c"
|
||||
apply(dashify, [1, 2], [3, 4]); // => "1,2-3-4"
|
||||
|
||||
@param f {function}
|
||||
function to be invoked
|
||||
</api>
|
||||
|
||||
<api name="flip">
|
||||
@function
|
||||
Returns function identical to given `f` but with flipped order
|
||||
of arguments.
|
||||
|
||||
const { flip } = require("sdk/lang/functional");
|
||||
|
||||
const append = (left, right) => left + " " + right;
|
||||
const prepend = flip(append);
|
||||
|
||||
append("hello", "world") // => "hello world"
|
||||
prepend("hello", "world") // => "world hello"
|
||||
|
||||
@param f {function}
|
||||
function whose arguments should to be flipped
|
||||
|
||||
@returns {function}
|
||||
function with flipped arguments
|
||||
</api>
|
||||
|
||||
<api name="when">
|
||||
@function
|
||||
Takes `p` predicate, `consequent` function and an optional
|
||||
`alternate` function and composes function that returns
|
||||
application of arguments over `consequent` if application over
|
||||
`p` is `true` otherwise returns application over `alternate`.
|
||||
If `alternate` is not a function returns `undefined`.
|
||||
|
||||
const { when } = require("sdk/lang/functional");
|
||||
|
||||
function Point(x, y) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
|
||||
const isPoint = x => x instanceof Point;
|
||||
|
||||
const inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
|
||||
|
||||
inc({}); // => undefined
|
||||
inc(new Point(0, 0)); // => { x: 1, y: 1 }
|
||||
|
||||
const axis = when(isPoint,
|
||||
({ x, y }) => [x, y],
|
||||
_ => [0, 0]);
|
||||
|
||||
axis(new Point(1, 4)); // => [1, 4]
|
||||
axis({ foo: "bar" }); // => [0, 0]
|
||||
|
||||
@param p {function}
|
||||
predicate function whose return value determines to which
|
||||
function be delegated control.
|
||||
|
||||
@param consequent {function}
|
||||
function to which arguments are applied if `predicate` returned
|
||||
`true`.
|
||||
|
||||
@param alternate {function}
|
||||
function to which arguments are applied if `predicate` returned
|
||||
`false`.
|
||||
|
||||
@returns {object|string|number|function}
|
||||
Return value from `consequent` if `p` returned `true` or return
|
||||
value from `alternate` if `p` returned `false`. If `alternate`
|
||||
is not provided and `p` returned `false` then `undefined` is
|
||||
returned.
|
||||
</api>
|
||||
|
||||
|
||||
<api name="chainable">
|
||||
@function
|
||||
Creates a version of the input function that will return `this`.
|
||||
|
||||
let { chain } = require("sdk/lang/functional");
|
||||
const { chainable } = require("sdk/lang/functional");
|
||||
|
||||
function Person (age) { this.age = age; }
|
||||
Person.prototype.happyBirthday = chain(function () this.age++);
|
||||
Person.prototype.happyBirthday = chainable(function() {
|
||||
return this.age++
|
||||
});
|
||||
|
||||
let person = new Person(30);
|
||||
const person = new Person(30);
|
||||
|
||||
person
|
||||
.happyBirthday()
|
||||
|
@ -311,7 +445,126 @@ Creates a version of the input function that will return `this`.
|
|||
The wrapped function that executes `fn` and returns `this`.
|
||||
</api>
|
||||
|
||||
<api name="cache">
|
||||
<api name="field">
|
||||
@function
|
||||
An alias for [once](modules/sdk/lang/functional.html#once(fn)).
|
||||
|
||||
Takes field `name` and `target` and returns value of that field.
|
||||
If `target` is `null` or `undefined` it would be returned back
|
||||
instead of attempt to access it's field. Function is implicitly
|
||||
curried, this allows accessor function generation by calling it
|
||||
with only `name` argument.
|
||||
|
||||
const { field } = require("sdk/lang/functional");
|
||||
|
||||
field("x", { x: 1, y: 2}); // => 1
|
||||
field("x")({ x: 1 }); // => 1
|
||||
field("x", { y: 2 }); // => undefiend
|
||||
|
||||
const getX = field("x");
|
||||
getX({ x: 1 }); // => 1
|
||||
getX({ y: 1 }); // => undefined
|
||||
getX(null); // => null
|
||||
|
||||
@param name {string}
|
||||
Name of the field to be returned
|
||||
|
||||
@param target {object}
|
||||
Target to get a field by the given `name` from
|
||||
|
||||
@returns {object|function|string|number|boolean}
|
||||
Field value
|
||||
</api>
|
||||
|
||||
<api name="query">
|
||||
@function
|
||||
|
||||
Takes `.` delimited string representing `path` to a nested field
|
||||
and a `target` to get it from. For convinience function is
|
||||
implicitly curried, there for accessors can be created by invoking
|
||||
it with just a `path` argument.
|
||||
|
||||
const { query } = require("sdk/lang/functional");
|
||||
|
||||
query("x", { x: 1, y: 2}); // => 1
|
||||
query("top.x", { x: 1 }); // => undefiend
|
||||
query("top.x", { top: { x: 2 } }); // => 2
|
||||
|
||||
const topX = query("top.x");
|
||||
topX({ top: { x: 1 } }); // => 1
|
||||
topX({ y: 1 }); // => undefined
|
||||
topX(null); // => null
|
||||
|
||||
@param path {string}
|
||||
`.` delimited path to a field
|
||||
|
||||
@param target {object}
|
||||
Target to get a field by the given `name` from
|
||||
|
||||
@returns {object|function|string|number|boolean}
|
||||
Field value
|
||||
</api>
|
||||
|
||||
<api name="isInstance">
|
||||
@function
|
||||
|
||||
Takes `Type` (constructor function) and a `value` and returns
|
||||
`true` if `value` is instance of the given `Type`. Function is
|
||||
implicitly curried this allows predicate generation by calling
|
||||
function with just first argument.
|
||||
|
||||
const { isInstance } = require("sdk/lang/functional");
|
||||
|
||||
function X() {}
|
||||
function Y() {}
|
||||
let isX = isInstance(X);
|
||||
|
||||
isInstance(X, new X); // true
|
||||
isInstance(X)(new X); // true
|
||||
isInstance(X, new Y); // false
|
||||
isInstance(X)(new Y); // false
|
||||
|
||||
isX(new X); // true
|
||||
isX(new Y); // false
|
||||
|
||||
@param Type {function}
|
||||
Type (constructor function)
|
||||
|
||||
@param instance {object}
|
||||
Instance to test
|
||||
|
||||
@returns {boolean}
|
||||
</api>
|
||||
|
||||
<api name="is">
|
||||
@function
|
||||
|
||||
Functions takes `expected` and `actual` values and returns `true` if
|
||||
`expected === actual`. If invoked with just one argument returns pratially
|
||||
applied function, which can be invoked to provide a second argument, this
|
||||
is handy with `map`, `filter` and other high order functions:
|
||||
|
||||
const { is } = require("sdk/util/oops");
|
||||
[ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
|
||||
|
||||
@param expected {object|string|number|boolean}
|
||||
@param actual {object|string|number|boolean}
|
||||
|
||||
@returns {boolean}
|
||||
</api>
|
||||
|
||||
<api name="isnt">
|
||||
@function
|
||||
|
||||
Functions takes `expected` and `actual` values and returns `true` if
|
||||
`expected !== actual`. If invoked with just one argument returns pratially
|
||||
applied function, which can be invoked with a second argument, which is
|
||||
handy with `map`, `filter` and other high order functions:
|
||||
|
||||
const { isnt } = require("sdk/util/oops");
|
||||
[ 1, 0, 1, 0, 1 ].map(isnt(0)) // => [ true, false, true, false, true ]
|
||||
|
||||
@param expected {object|string|number|boolean}
|
||||
@param actual {object|string|number|boolean}
|
||||
|
||||
@returns {boolean}
|
||||
</api>
|
||||
|
|
|
@ -9,7 +9,7 @@ module.metadata = {
|
|||
};
|
||||
|
||||
const { on, once, off, setListeners } = require('./core');
|
||||
const { method, chain } = require('../lang/functional');
|
||||
const { method, chainable } = require('../lang/functional');
|
||||
const { Class } = require('../core/heritage');
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ const EventTarget = Class({
|
|||
* console.log('data received: ' + data)
|
||||
* })
|
||||
*/
|
||||
on: chain(method(on)),
|
||||
on: chainable(method(on)),
|
||||
/**
|
||||
* Registers an event `listener` that is called once the next time an event
|
||||
* of the specified `type` is emitted.
|
||||
|
@ -52,7 +52,7 @@ const EventTarget = Class({
|
|||
* @param {Function} listener
|
||||
* The listener function that processes the event.
|
||||
*/
|
||||
once: chain(method(once)),
|
||||
once: chainable(method(once)),
|
||||
/**
|
||||
* Removes an event `listener` for the given event `type`.
|
||||
* @param {String} type
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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/. */
|
||||
|
||||
// Disclaimer: Most of the functions in this module implement APIs from
|
||||
// Disclaimer: Some of the functions in this module implement APIs from
|
||||
// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for
|
||||
// those goes to him.
|
||||
|
||||
|
@ -12,19 +12,33 @@ module.metadata = {
|
|||
"stability": "unstable"
|
||||
};
|
||||
|
||||
const { setImmediate, setTimeout } = require("../timers");
|
||||
const { deprecateFunction } = require("../util/deprecate");
|
||||
const { setImmediate, setTimeout } = require("../timers");
|
||||
|
||||
const arity = f => f.arity || f.length;
|
||||
|
||||
const name = f => f.displayName || f.name;
|
||||
|
||||
const derive = (f, source) => {
|
||||
f.displayName = name(source);
|
||||
f.arity = arity(source);
|
||||
return f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes `lambda` function and returns a method. When returned method is
|
||||
* invoked it calls wrapped `lambda` and passes `this` as a first argument
|
||||
* and given argument as rest.
|
||||
* Takes variadic numeber of functions and returns composed one.
|
||||
* Returned function pushes `this` pseudo-variable to the head
|
||||
* of the passed arguments and invokes all the functions from
|
||||
* left to right passing same arguments to them. Composite function
|
||||
* returns return value of the right most funciton.
|
||||
*/
|
||||
function method(lambda) {
|
||||
return function method() {
|
||||
return lambda.apply(null, [this].concat(Array.slice(arguments)));
|
||||
}
|
||||
}
|
||||
const method = (...lambdas) => {
|
||||
return function method(...args) {
|
||||
args.unshift(this);
|
||||
return lambdas.reduce((_, lambda) => lambda.apply(this, args),
|
||||
void(0));
|
||||
};
|
||||
};
|
||||
exports.method = method;
|
||||
|
||||
/**
|
||||
|
@ -34,24 +48,13 @@ exports.method = method;
|
|||
* function is reused, instead of creating a new one each time. This also allows
|
||||
* to use this functions as event listeners.
|
||||
*/
|
||||
function defer(f) {
|
||||
return function deferred() setImmediate(invoke, f, arguments, this);
|
||||
}
|
||||
const defer = f => derive(function(...args) {
|
||||
setImmediate(invoke, f, args, this);
|
||||
}, f);
|
||||
exports.defer = defer;
|
||||
// Exporting `remit` alias as `defer` may conflict with promises.
|
||||
exports.remit = defer;
|
||||
|
||||
/*
|
||||
* Takes a funtion and returns a wrapped function that returns `this`
|
||||
*/
|
||||
function chain(f) {
|
||||
return function chainable(...args) {
|
||||
f.apply(this, args);
|
||||
return this;
|
||||
};
|
||||
}
|
||||
exports.chain = chain;
|
||||
|
||||
/**
|
||||
* Invokes `callee` by passing `params` as an arguments and `self` as `this`
|
||||
* pseudo-variable. Returns value that is returned by a callee.
|
||||
|
@ -62,7 +65,7 @@ exports.chain = chain;
|
|||
* @param {Object} self
|
||||
* Object to be passed as a `this` pseudo variable.
|
||||
*/
|
||||
function invoke(callee, params, self) callee.apply(self, params);
|
||||
const invoke = (callee, params, self) => callee.apply(self, params);
|
||||
exports.invoke = invoke;
|
||||
|
||||
/**
|
||||
|
@ -74,14 +77,16 @@ exports.invoke = invoke;
|
|||
*
|
||||
* @returns The new function with binded values
|
||||
*/
|
||||
function partial(fn) {
|
||||
if (typeof fn !== "function")
|
||||
throw new TypeError(String(fn) + " is not a function");
|
||||
const partial = (f, ...curried) => {
|
||||
if (typeof(f) !== "function")
|
||||
throw new TypeError(String(f) + " is not a function");
|
||||
|
||||
let args = Array.slice(arguments, 1);
|
||||
|
||||
return function() fn.apply(this, args.concat(Array.slice(arguments)));
|
||||
}
|
||||
let fn = derive(function(...args) {
|
||||
return f.apply(this, curried.concat(args));
|
||||
}, f);
|
||||
fn.arity = arity(f) - curried.length;
|
||||
return fn;
|
||||
};
|
||||
exports.partial = partial;
|
||||
|
||||
/**
|
||||
|
@ -98,12 +103,11 @@ exports.partial = partial;
|
|||
* console.log(sum(2, 2)) // 4
|
||||
* console.log(sum(2)(4)) // 6
|
||||
*/
|
||||
var curry = new function() {
|
||||
function currier(fn, arity, params) {
|
||||
const curry = new function() {
|
||||
const currier = (fn, arity, params) => {
|
||||
// Function either continues to curry arguments or executes function
|
||||
// if desired arguments have being collected.
|
||||
return function curried() {
|
||||
var input = Array.slice(arguments);
|
||||
const curried = function(...input) {
|
||||
// Prepend all curried arguments to the given arguments.
|
||||
if (params) input.unshift.apply(input, params);
|
||||
// If expected number of arguments has being collected invoke fn,
|
||||
|
@ -111,11 +115,12 @@ var curry = new function() {
|
|||
return (input.length >= arity) ? fn.apply(this, input) :
|
||||
currier(fn, arity, input);
|
||||
};
|
||||
}
|
||||
curried.arity = arity - (params ? params.length : 0);
|
||||
|
||||
return function curry(fn) {
|
||||
return currier(fn, fn.length);
|
||||
}
|
||||
return curried;
|
||||
};
|
||||
|
||||
return fn => currier(fn, arity(fn));
|
||||
};
|
||||
exports.curry = curry;
|
||||
|
||||
|
@ -131,12 +136,12 @@ exports.curry = curry;
|
|||
*
|
||||
* welcome('moe'); // => 'hi: moe!'
|
||||
*/
|
||||
function compose() {
|
||||
let lambdas = Array.slice(arguments);
|
||||
return function composed() {
|
||||
let args = Array.slice(arguments), index = lambdas.length;
|
||||
function compose(...lambdas) {
|
||||
return function composed(...args) {
|
||||
let index = lambdas.length;
|
||||
while (0 <= --index)
|
||||
args = [ lambdas[index].apply(this, args) ];
|
||||
args = [lambdas[index].apply(this, args)];
|
||||
|
||||
return args[0];
|
||||
};
|
||||
}
|
||||
|
@ -155,16 +160,15 @@ exports.compose = compose;
|
|||
*
|
||||
* hello(); // => 'before, hello: moe, after'
|
||||
*/
|
||||
function wrap(f, wrapper) {
|
||||
return function wrapped()
|
||||
wrapper.apply(this, [ f ].concat(Array.slice(arguments)))
|
||||
};
|
||||
const wrap = (f, wrapper) => derive(function wrapped(...args) {
|
||||
return wrapper.apply(this, [f].concat(args));
|
||||
}, f);
|
||||
exports.wrap = wrap;
|
||||
|
||||
/**
|
||||
* Returns the same value that is used as the argument. In math: f(x) = x
|
||||
*/
|
||||
function identity(value) value
|
||||
const identity = value => value;
|
||||
exports.identity = identity;
|
||||
|
||||
/**
|
||||
|
@ -174,14 +178,25 @@ exports.identity = identity;
|
|||
* the arguments to the original function. The default hashFunction just uses
|
||||
* the first argument to the memoized function as the key.
|
||||
*/
|
||||
function memoize(f, hasher) {
|
||||
const memoize = (f, hasher) => {
|
||||
let memo = Object.create(null);
|
||||
let cache = new WeakMap();
|
||||
hasher = hasher || identity;
|
||||
return function memoizer() {
|
||||
let key = hasher.apply(this, arguments);
|
||||
return key in memo ? memo[key] : (memo[key] = f.apply(this, arguments));
|
||||
};
|
||||
}
|
||||
return derive(function memoizer(...args) {
|
||||
const key = hasher.apply(this, args);
|
||||
const type = typeof(key);
|
||||
if (key && (type === "object" || type === "function")) {
|
||||
if (!cache.has(key))
|
||||
cache.set(key, f.apply(this, args));
|
||||
return cache.get(key);
|
||||
}
|
||||
else {
|
||||
if (!(key in memo))
|
||||
memo[key] = f.apply(this, args);
|
||||
return memo[key];
|
||||
}
|
||||
}, f);
|
||||
};
|
||||
exports.memoize = memoize;
|
||||
|
||||
/**
|
||||
|
@ -189,9 +204,8 @@ exports.memoize = memoize;
|
|||
* the optional arguments, they will be forwarded on to the function when it is
|
||||
* invoked.
|
||||
*/
|
||||
function delay(f, ms) {
|
||||
let args = Array.slice(arguments, 2);
|
||||
setTimeout(function(context) { return f.apply(context, args); }, ms, this);
|
||||
const delay = function delay(f, ms, ...args) {
|
||||
setTimeout(() => f.apply(this, args), ms);
|
||||
};
|
||||
exports.delay = delay;
|
||||
|
||||
|
@ -201,10 +215,116 @@ exports.delay = delay;
|
|||
* the original call. Useful for initialization functions, instead of having to
|
||||
* set a boolean flag and then check it later.
|
||||
*/
|
||||
function once(f) {
|
||||
const once = f => {
|
||||
let ran = false, cache;
|
||||
return function() ran ? cache : (ran = true, cache = f.apply(this, arguments))
|
||||
return derive(function(...args) {
|
||||
return ran ? cache : (ran = true, cache = f.apply(this, args));
|
||||
}, f);
|
||||
};
|
||||
exports.once = once;
|
||||
// export cache as once will may be conflicting with event once a lot.
|
||||
exports.cache = once;
|
||||
|
||||
// Takes a `f` function and returns a function that takes the same
|
||||
// arguments as `f`, has the same effects, if any, and returns the
|
||||
// opposite truth value.
|
||||
const complement = f => derive(function(...args) {
|
||||
return args.length < arity(f) ? complement(partial(f, ...args)) :
|
||||
!f.apply(this, args);
|
||||
}, f);
|
||||
exports.complement = complement;
|
||||
|
||||
// Constructs function that returns `x` no matter what is it
|
||||
// invoked with.
|
||||
const constant = x => _ => x;
|
||||
exports.constant = constant;
|
||||
|
||||
// Takes `p` predicate, `consequent` function and an optional
|
||||
// `alternate` function and composes function that returns
|
||||
// application of arguments over `consequent` if application over
|
||||
// `p` is `true` otherwise returns application over `alternate`.
|
||||
// If `alternate` is not a function returns `undefined`.
|
||||
const when = (p, consequent, alternate) => {
|
||||
if (typeof(alternate) !== "function" && alternate !== void(0))
|
||||
throw TypeError("alternate must be a function");
|
||||
if (typeof(consequent) !== "function")
|
||||
throw TypeError("consequent must be a function");
|
||||
|
||||
return function(...args) {
|
||||
return p.apply(this, args) ?
|
||||
consequent.apply(this, args) :
|
||||
alternate && alternate.apply(this, args);
|
||||
};
|
||||
};
|
||||
exports.when = when;
|
||||
|
||||
// Apply function that behaves as `apply` does in lisp:
|
||||
// apply(f, x, [y, z]) => f.apply(f, [x, y, z])
|
||||
// apply(f, x) => f.apply(f, [x])
|
||||
const apply = (f, ...rest) => f.apply(f, rest.concat(rest.pop()));
|
||||
exports.apply = apply;
|
||||
|
||||
// Returns function identical to given `f` but with flipped order
|
||||
// of arguments.
|
||||
const flip = f => derive(function(...args) {
|
||||
return f.apply(this, args.reverse());
|
||||
}, f);
|
||||
exports.flip = flip;
|
||||
|
||||
|
||||
// Takes field `name` and `target` and returns value of that field.
|
||||
// If `target` is `null` or `undefined` it would be returned back
|
||||
// instead of attempt to access it's field. Function is implicitly
|
||||
// curried, this allows accessor function generation by calling it
|
||||
// with only `name` argument.
|
||||
const field = curry((name, target) =>
|
||||
// Note: Permisive `==` is intentional.
|
||||
target == null ? target : target[name]);
|
||||
exports.field = field;
|
||||
|
||||
// Takes `.` delimited string representing `path` to a nested field
|
||||
// and a `target` to get it from. For convinience function is
|
||||
// implicitly curried, there for accessors can be created by invoking
|
||||
// it with just a `path` argument.
|
||||
const query = curry((path, target) => {
|
||||
const names = path.split(".");
|
||||
const count = names.length;
|
||||
let index = 0;
|
||||
let result = target;
|
||||
// Note: Permisive `!=` is intentional.
|
||||
while (result != null && index < count) {
|
||||
result = result[names[index]];
|
||||
index = index + 1;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
exports.query = query;
|
||||
|
||||
// Takes `Type` (constructor function) and a `value` and returns
|
||||
// `true` if `value` is instance of the given `Type`. Function is
|
||||
// implicitly curried this allows predicate generation by calling
|
||||
// function with just first argument.
|
||||
const isInstance = curry((Type, value) => value instanceof Type);
|
||||
exports.isInstance = isInstance;
|
||||
|
||||
/*
|
||||
* Takes a funtion and returns a wrapped function that returns `this`
|
||||
*/
|
||||
const chainable = f => derive(function(...args) {
|
||||
f.apply(this, args);
|
||||
return this;
|
||||
}, f);
|
||||
exports.chainable = chainable;
|
||||
exports.chain =
|
||||
deprecateFunction(chainable, "Function `chain` was renamed to `chainable`");
|
||||
|
||||
// Functions takes `expected` and `actual` values and returns `true` if
|
||||
// `expected === actual`. Returns curried function if called with less then
|
||||
// two arguments.
|
||||
//
|
||||
// [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ]
|
||||
const is = curry((expected, actual) => actual === expected);
|
||||
exports.is = is;
|
||||
|
||||
const isnt = complement(is);
|
||||
exports.isnt = isnt;
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
/* 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 { setTimeout } = require('sdk/timers');
|
||||
const utils = require('sdk/lang/functional');
|
||||
const { invoke, defer, partial, compose, memoize, once, delay, wrap, curry, chain } = utils;
|
||||
const { invoke, defer, partial, compose, memoize, once, is, isnt,
|
||||
delay, wrap, curry, chainable, field, query, isInstance } = utils;
|
||||
const { LoaderWithHookedConsole } = require('sdk/test/loader');
|
||||
|
||||
exports['test forwardApply'] = function(assert) {
|
||||
function sum(b, c) this.a + b + c
|
||||
function sum(b, c) { return this.a + b + c; }
|
||||
assert.equal(invoke(sum, [2, 3], { a: 1 }), 6,
|
||||
'passed arguments and pseoude-variable are used');
|
||||
|
||||
assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7,
|
||||
'bounded `this` pseoudo variable is used');
|
||||
}
|
||||
};
|
||||
|
||||
exports['test deferred function'] = function(assert, done) {
|
||||
let nextTurn = false;
|
||||
|
@ -26,13 +27,13 @@ exports['test deferred function'] = function(assert, done) {
|
|||
done();
|
||||
}
|
||||
|
||||
let fixture = { a: 1, method: defer(sum) }
|
||||
let fixture = { a: 1, method: defer(sum) };
|
||||
fixture.method(2, 3);
|
||||
nextTurn = true;
|
||||
};
|
||||
|
||||
exports['test partial function'] = function(assert) {
|
||||
function sum(b, c) this.a + b + c;
|
||||
function sum(b, c) { return this.a + b + c; }
|
||||
|
||||
let foo = { a : 5 };
|
||||
|
||||
|
@ -68,11 +69,11 @@ exports['test compose'] = function(assert) {
|
|||
let target = {
|
||||
name: 'Joe',
|
||||
greet: compose(function exclaim(sentence) {
|
||||
return sentence + '!'
|
||||
return sentence + '!';
|
||||
}, function(title) {
|
||||
return 'hi : ' + title + ' ' + this.name;
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
assert.equal(target.greet('Mr'), 'hi : Mr Joe!',
|
||||
'this can be passed in');
|
||||
|
@ -106,7 +107,7 @@ exports['test wrap'] = function(assert) {
|
|||
|
||||
assert.equal(target.hi(), 'Hello Matteo', 'works with this');
|
||||
|
||||
function noop() { };
|
||||
function noop() { }
|
||||
let wrapped = wrap(noop, function(f) {
|
||||
return Array.slice(arguments);
|
||||
});
|
||||
|
@ -117,7 +118,7 @@ exports['test wrap'] = function(assert) {
|
|||
};
|
||||
|
||||
exports['test memoize'] = function(assert) {
|
||||
function fib(n) n < 2 ? n : fib(n - 1) + fib(n - 2)
|
||||
const fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2);
|
||||
let fibnitro = memoize(fib);
|
||||
|
||||
assert.equal(fib(10), 55,
|
||||
|
@ -125,7 +126,7 @@ exports['test memoize'] = function(assert) {
|
|||
assert.equal(fibnitro(10), 55,
|
||||
'a memoized version of fibonacci produces identical results');
|
||||
|
||||
function o(key, value) { return value; };
|
||||
function o(key, value) { return value; }
|
||||
let oo = memoize(o), v1 = {}, v2 = {};
|
||||
|
||||
|
||||
|
@ -136,12 +137,12 @@ exports['test memoize'] = function(assert) {
|
|||
assert.notEqual(oo(1), oo(2), 'values do not override');
|
||||
assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized');
|
||||
|
||||
let get = memoize(function(attribute) this[attribute])
|
||||
let target = { name: 'Bob', get: get }
|
||||
let get = memoize(function(attribute) { return this[attribute]; });
|
||||
let target = { name: 'Bob', get: get };
|
||||
|
||||
assert.equal(target.get('name'), 'Bob', 'has correct `this`');
|
||||
assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob',
|
||||
'name is memoized')
|
||||
'name is memoized');
|
||||
assert.equal(get('name'), 'Bob', 'once memoized can be called without this');
|
||||
};
|
||||
|
||||
|
@ -155,13 +156,13 @@ exports['test delay'] = function(assert, done) {
|
|||
};
|
||||
|
||||
exports['test delay with this'] = function(assert, done) {
|
||||
let context = {}
|
||||
let context = {};
|
||||
delay.call(context, function(name) {
|
||||
assert.equal(this, context, 'this was passed in');
|
||||
assert.equal(name, 'Tom', 'argument was passed in');
|
||||
done();
|
||||
}, 10, 'Tom');
|
||||
}
|
||||
};
|
||||
|
||||
exports['test once'] = function(assert) {
|
||||
let n = 0;
|
||||
|
@ -172,7 +173,12 @@ exports['test once'] = function(assert) {
|
|||
|
||||
assert.equal(n, 1, 'only incremented once');
|
||||
|
||||
let target = { state: 0, update: once(function() this.state ++ ) };
|
||||
let target = {
|
||||
state: 0,
|
||||
update: once(function() {
|
||||
return this.state ++;
|
||||
})
|
||||
};
|
||||
|
||||
target.update();
|
||||
target.update();
|
||||
|
@ -182,7 +188,7 @@ exports['test once'] = function(assert) {
|
|||
|
||||
exports['test once with argument'] = function(assert) {
|
||||
let n = 0;
|
||||
let increment = once(function(a) n++);
|
||||
let increment = once(a => n++);
|
||||
|
||||
increment();
|
||||
increment('foo');
|
||||
|
@ -195,11 +201,121 @@ exports['test once with argument'] = function(assert) {
|
|||
assert.equal(n, 1, 'only incremented once');
|
||||
};
|
||||
|
||||
exports['test chain'] = function (assert) {
|
||||
exports['test complement'] = assert => {
|
||||
let { complement } = require("sdk/lang/functional");
|
||||
|
||||
let isOdd = x => Boolean(x % 2);
|
||||
|
||||
assert.equal(isOdd(1), true);
|
||||
assert.equal(isOdd(2), false);
|
||||
|
||||
let isEven = complement(isOdd);
|
||||
|
||||
assert.equal(isEven(1), false);
|
||||
assert.equal(isEven(2), true);
|
||||
|
||||
let foo = {};
|
||||
let isFoo = function() { return this === foo; };
|
||||
let insntFoo = complement(isFoo);
|
||||
|
||||
assert.equal(insntFoo.call(foo), false);
|
||||
assert.equal(insntFoo.call({}), true);
|
||||
};
|
||||
|
||||
exports['test constant'] = assert => {
|
||||
let { constant } = require("sdk/lang/functional");
|
||||
|
||||
let one = constant(1);
|
||||
|
||||
assert.equal(one(1), 1);
|
||||
assert.equal(one(2), 1);
|
||||
};
|
||||
|
||||
exports['test apply'] = assert => {
|
||||
let { apply } = require("sdk/lang/functional");
|
||||
|
||||
let dashify = (...args) => args.join("-");
|
||||
|
||||
assert.equal(apply(dashify, 1, [2, 3]), "1-2-3");
|
||||
assert.equal(apply(dashify, "a"), "a");
|
||||
assert.equal(apply(dashify, ["a", "b"]), "a-b");
|
||||
assert.equal(apply(dashify, ["a", "b"], "c"), "a,b-c");
|
||||
assert.equal(apply(dashify, [1, 2], [3, 4]), "1,2-3-4");
|
||||
};
|
||||
|
||||
exports['test flip'] = assert => {
|
||||
let { flip } = require("sdk/lang/functional");
|
||||
|
||||
let append = (left, right) => left + " " + right;
|
||||
let prepend = flip(append);
|
||||
|
||||
assert.equal(append("hello", "world"), "hello world");
|
||||
assert.equal(prepend("hello", "world"), "world hello");
|
||||
|
||||
let wrap = function(left, right) {
|
||||
return left + " " + this + " " + right;
|
||||
};
|
||||
let invertWrap = flip(wrap);
|
||||
|
||||
assert.equal(wrap.call("@", "hello", "world"), "hello @ world");
|
||||
assert.equal(invertWrap.call("@", "hello", "world"), "world @ hello");
|
||||
|
||||
let reverse = flip((...args) => args);
|
||||
|
||||
assert.deepEqual(reverse(1, 2, 3, 4), [4, 3, 2, 1]);
|
||||
assert.deepEqual(reverse(1), [1]);
|
||||
assert.deepEqual(reverse(), []);
|
||||
|
||||
// currying still works
|
||||
let prependr = curry(prepend);
|
||||
|
||||
assert.equal(prependr("hello", "world"), "world hello");
|
||||
assert.equal(prependr("hello")("world"), "world hello");
|
||||
};
|
||||
|
||||
exports["test when"] = assert => {
|
||||
let { when } = require("sdk/lang/functional");
|
||||
|
||||
let areNums = (...xs) => xs.every(x => typeof(x) === "number");
|
||||
|
||||
let sum = when(areNums, (...xs) => xs.reduce((y, x) => x + y, 0));
|
||||
|
||||
assert.equal(sum(1, 2, 3), 6);
|
||||
assert.equal(sum(1, 2, "3"), undefined);
|
||||
|
||||
let multiply = when(areNums,
|
||||
(...xs) => xs.reduce((y, x) => x * y, 1),
|
||||
(...xs) => xs);
|
||||
|
||||
assert.equal(multiply(2), 2);
|
||||
assert.equal(multiply(2, 3), 6);
|
||||
assert.deepEqual(multiply(2, "4"), [2, "4"]);
|
||||
|
||||
function Point(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
let isPoint = x => x instanceof Point;
|
||||
|
||||
let inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1));
|
||||
|
||||
assert.equal(inc({}), undefined);
|
||||
assert.deepEqual(inc(new Point(0, 0)), { x: 1, y: 1 });
|
||||
|
||||
let axis = when(isPoint,
|
||||
({ x, y }) => [x, y],
|
||||
_ => [0, 0]);
|
||||
|
||||
assert.deepEqual(axis(new Point(1, 4)), [1, 4]);
|
||||
assert.deepEqual(axis({ foo: "bar" }), [0, 0]);
|
||||
};
|
||||
|
||||
exports["test chainable"] = function(assert) {
|
||||
let Player = function () { this.volume = 5; };
|
||||
Player.prototype = {
|
||||
setBand: chain(function (band) this.band = band),
|
||||
incVolume: chain(function () this.volume++)
|
||||
setBand: chainable(function (band) { return (this.band = band); }),
|
||||
incVolume: chainable(function () { return this.volume++; })
|
||||
};
|
||||
let player = new Player();
|
||||
player
|
||||
|
@ -210,4 +326,97 @@ exports['test chain'] = function (assert) {
|
|||
assert.equal(player.volume, 11, 'accepts no arguments in chain');
|
||||
};
|
||||
|
||||
exports["test field"] = assert => {
|
||||
let Num = field("constructor", 0);
|
||||
assert.equal(Num.name, Number.name);
|
||||
assert.ok(typeof(Num), "function");
|
||||
|
||||
let x = field("x");
|
||||
|
||||
[
|
||||
[field("foo", { foo: 1 }), 1],
|
||||
[field("foo")({ foo: 1 }), 1],
|
||||
[field("bar", {}), undefined],
|
||||
[field("bar")({}), undefined],
|
||||
[field("hey", undefined), undefined],
|
||||
[field("hey")(undefined), undefined],
|
||||
[field("how", null), null],
|
||||
[field("how")(null), null],
|
||||
[x(1), undefined],
|
||||
[x(undefined), undefined],
|
||||
[x(null), null],
|
||||
[x({ x: 1 }), 1],
|
||||
[x({ x: 2 }), 2],
|
||||
].forEach(([actual, expected]) => assert.equal(actual, expected));
|
||||
};
|
||||
|
||||
exports["test query"] = assert => {
|
||||
let Num = query("constructor", 0);
|
||||
assert.equal(Num.name, Number.name);
|
||||
assert.ok(typeof(Num), "function");
|
||||
|
||||
let x = query("x");
|
||||
let xy = query("x.y");
|
||||
|
||||
[
|
||||
[query("foo", { foo: 1 }), 1],
|
||||
[query("foo")({ foo: 1 }), 1],
|
||||
[query("foo.bar", { foo: { bar: 2 } }), 2],
|
||||
[query("foo.bar")({ foo: { bar: 2 } }), 2],
|
||||
[query("foo.bar", { foo: 1 }), undefined],
|
||||
[query("foo.bar")({ foo: 1 }), undefined],
|
||||
[x(1), undefined],
|
||||
[x(undefined), undefined],
|
||||
[x(null), null],
|
||||
[x({ x: 1 }), 1],
|
||||
[x({ x: 2 }), 2],
|
||||
[xy(1), undefined],
|
||||
[xy(undefined), undefined],
|
||||
[xy(null), null],
|
||||
[xy({ x: 1 }), undefined],
|
||||
[xy({ x: 2 }), undefined],
|
||||
[xy({ x: { y: 1 } }), 1],
|
||||
[xy({ x: { y: 2 } }), 2]
|
||||
].forEach(([actual, expected]) => assert.equal(actual, expected));
|
||||
};
|
||||
|
||||
exports["test isInstance"] = assert => {
|
||||
function X() {}
|
||||
function Y() {}
|
||||
let isX = isInstance(X);
|
||||
|
||||
[
|
||||
isInstance(X, new X()),
|
||||
isInstance(X)(new X()),
|
||||
!isInstance(X, new Y()),
|
||||
!isInstance(X)(new Y()),
|
||||
isX(new X()),
|
||||
!isX(new Y())
|
||||
].forEach(x => assert.ok(x));
|
||||
};
|
||||
|
||||
exports["test is"] = assert => {
|
||||
|
||||
assert.deepEqual([ 1, 0, 1, 0, 1 ].map(is(1)),
|
||||
[ true, false, true, false, true ],
|
||||
"is can be partially applied");
|
||||
|
||||
assert.ok(is(1, 1));
|
||||
assert.ok(!is({}, {}));
|
||||
assert.ok(is()(1)()(1), "is is curried");
|
||||
assert.ok(!is()(1)()(2));
|
||||
};
|
||||
|
||||
exports["test isnt"] = assert => {
|
||||
|
||||
assert.deepEqual([ 1, 0, 1, 0, 1 ].map(isnt(0)),
|
||||
[ true, false, true, false, true ],
|
||||
"is can be partially applied");
|
||||
|
||||
assert.ok(!isnt(1, 1));
|
||||
assert.ok(isnt({}, {}));
|
||||
assert.ok(!isnt()(1)()(1));
|
||||
assert.ok(isnt()(1)()(2));
|
||||
};
|
||||
|
||||
require('test').run(exports);
|
||||
|
|
|
@ -1093,7 +1093,7 @@ pref("devtools.toolbox.footer.height", 250);
|
|||
pref("devtools.toolbox.sidebar.width", 500);
|
||||
pref("devtools.toolbox.host", "bottom");
|
||||
pref("devtools.toolbox.selectedTool", "webconsole");
|
||||
pref("devtools.toolbox.toolbarSpec", '["paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
|
||||
pref("devtools.toolbox.toolbarSpec", '["splitconsole", "paintflashing toggle","tilt toggle","scratchpad","resize toggle"]');
|
||||
pref("devtools.toolbox.sideEnabled", true);
|
||||
pref("devtools.toolbox.zoomValue", "1");
|
||||
|
||||
|
|
|
@ -2272,3 +2272,68 @@ gcli.addCommand({
|
|||
}
|
||||
});
|
||||
}(this));
|
||||
|
||||
/* CmdSplitConsole ------------------------------------------------------- */
|
||||
|
||||
(function(module) {
|
||||
/**
|
||||
* 'splitconsole' command (hidden)
|
||||
*/
|
||||
|
||||
gcli.addCommand({
|
||||
name: 'splitconsole',
|
||||
hidden: true,
|
||||
buttonId: "command-button-splitconsole",
|
||||
buttonClass: "command-button",
|
||||
tooltipText: gcli.lookup("splitconsoleTooltip"),
|
||||
state: {
|
||||
isChecked: function(aTarget) {
|
||||
let toolbox = gDevTools.getToolbox(aTarget);
|
||||
return toolbox &&
|
||||
toolbox.splitConsole;
|
||||
},
|
||||
onChange: function(aTarget, aChangeHandler) {
|
||||
eventEmitter.on("changed", aChangeHandler);
|
||||
},
|
||||
offChange: function(aTarget, aChangeHandler) {
|
||||
eventEmitter.off("changed", aChangeHandler);
|
||||
},
|
||||
},
|
||||
exec: function(args, context) {
|
||||
toggleSplitConsole(context);
|
||||
}
|
||||
});
|
||||
|
||||
function toggleSplitConsole(context) {
|
||||
let gBrowser = context.environment.chromeDocument.defaultView.gBrowser;
|
||||
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
||||
let toolbox = gDevTools.getToolbox(target);
|
||||
|
||||
if (!toolbox) {
|
||||
gDevTools.showToolbox(target, "inspector").then((toolbox) => {
|
||||
toolbox.toggleSplitConsole();
|
||||
});
|
||||
} else {
|
||||
toolbox.toggleSplitConsole();
|
||||
}
|
||||
}
|
||||
|
||||
let eventEmitter = new EventEmitter();
|
||||
function fireChange(tab) {
|
||||
eventEmitter.emit("changed", tab);
|
||||
}
|
||||
|
||||
gDevTools.on("toolbox-ready", (e, toolbox) => {
|
||||
if (!toolbox.target) {
|
||||
return;
|
||||
}
|
||||
let fireChangeForTab = fireChange.bind(this, toolbox.target.tab);
|
||||
toolbox.on("split-console", fireChangeForTab);
|
||||
toolbox.on("select", fireChangeForTab);
|
||||
toolbox.once("destroyed", () => {
|
||||
toolbox.off("split-console", fireChangeForTab);
|
||||
toolbox.off("select", fireChangeForTab);
|
||||
});
|
||||
});
|
||||
|
||||
}(this));
|
||||
|
|
|
@ -182,6 +182,13 @@ Toolbox.prototype = {
|
|||
return parseFloat(Services.prefs.getCharPref(ZOOM_PREF));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the toggled state of the split console
|
||||
*/
|
||||
get splitConsole() {
|
||||
return this._splitConsole;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the toolbox
|
||||
*/
|
||||
|
|
|
@ -63,22 +63,24 @@ function test()
|
|||
function getCurrentUIState()
|
||||
{
|
||||
let win = toolbox.doc.defaultView;
|
||||
let deck = toolbox.doc.getElementById("toolbox-deck");
|
||||
let webconsolePanel = toolbox.doc.getElementById("toolbox-panel-webconsole");
|
||||
let splitter = toolbox.doc.getElementById("toolbox-console-splitter");
|
||||
let deck = toolbox.doc.querySelector("#toolbox-deck");
|
||||
let webconsolePanel = toolbox.doc.querySelector("#toolbox-panel-webconsole");
|
||||
let splitter = toolbox.doc.querySelector("#toolbox-console-splitter");
|
||||
|
||||
let containerHeight = parseFloat(win.getComputedStyle(deck.parentNode).getPropertyValue("height"));
|
||||
let deckHeight = parseFloat(win.getComputedStyle(deck).getPropertyValue("height"));
|
||||
let webconsoleHeight = parseFloat(win.getComputedStyle(webconsolePanel).getPropertyValue("height"));
|
||||
let splitterVisibility = !splitter.getAttribute("hidden");
|
||||
let openedConsolePanel = toolbox.currentToolId === "webconsole";
|
||||
let cmdButton = toolbox.doc.querySelector("#command-button-splitconsole");
|
||||
|
||||
return {
|
||||
deckHeight: deckHeight,
|
||||
containerHeight: containerHeight,
|
||||
webconsoleHeight: webconsoleHeight,
|
||||
splitterVisibility: splitterVisibility,
|
||||
openedConsolePanel: openedConsolePanel
|
||||
openedConsolePanel: openedConsolePanel,
|
||||
buttonSelected: cmdButton.hasAttribute("checked")
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -97,6 +99,7 @@ function test()
|
|||
ok (currentUIState.deckHeight > 0, "Deck has a height > 0 when console is split");
|
||||
ok (currentUIState.webconsoleHeight > 0, "Web console has a height > 0 when console is split");
|
||||
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
|
||||
ok (currentUIState.buttonSelected, "The command button is selected");
|
||||
|
||||
openPanel("webconsole").then(() => {
|
||||
|
||||
|
@ -106,6 +109,7 @@ function test()
|
|||
is (currentUIState.deckHeight, 0, "Deck has a height == 0 when console is opened.");
|
||||
is (currentUIState.webconsoleHeight, currentUIState.containerHeight, "Web console is full height.");
|
||||
ok (currentUIState.openedConsolePanel, "The console panel is the current tool");
|
||||
ok (currentUIState.buttonSelected, "The command button is still selected.");
|
||||
|
||||
// Make sure splitting console does nothing while webconsole is opened
|
||||
toolbox.toggleSplitConsole();
|
||||
|
@ -116,6 +120,7 @@ function test()
|
|||
is (currentUIState.deckHeight, 0, "Deck has a height == 0 when console is opened.");
|
||||
is (currentUIState.webconsoleHeight, currentUIState.containerHeight, "Web console is full height.");
|
||||
ok (currentUIState.openedConsolePanel, "The console panel is the current tool");
|
||||
ok (currentUIState.buttonSelected, "The command button is still selected.");
|
||||
|
||||
// Make sure that split state is saved after opening another panel
|
||||
openPanel("inspector").then(() => {
|
||||
|
@ -124,6 +129,7 @@ function test()
|
|||
ok (currentUIState.deckHeight > 0, "Deck has a height > 0 when console is split");
|
||||
ok (currentUIState.webconsoleHeight > 0, "Web console has a height > 0 when console is split");
|
||||
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
|
||||
ok (currentUIState.buttonSelected, "The command button is still selected.");
|
||||
|
||||
toolbox.toggleSplitConsole();
|
||||
deferred.resolve();
|
||||
|
@ -163,6 +169,7 @@ function test()
|
|||
is (currentUIState.deckHeight, currentUIState.containerHeight, "Deck has a height > 0 by default");
|
||||
is (currentUIState.webconsoleHeight, 0, "Web console is collapsed by default");
|
||||
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
|
||||
ok (!currentUIState.buttonSelected, "The command button is not selected.");
|
||||
|
||||
toolbox.toggleSplitConsole();
|
||||
|
||||
|
@ -175,6 +182,7 @@ function test()
|
|||
currentUIState.containerHeight,
|
||||
"Everything adds up to container height");
|
||||
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
|
||||
ok (currentUIState.buttonSelected, "The command button is selected.");
|
||||
|
||||
toolbox.toggleSplitConsole();
|
||||
|
||||
|
@ -184,9 +192,9 @@ function test()
|
|||
is (currentUIState.deckHeight, currentUIState.containerHeight, "Deck has a height > 0 after toggling");
|
||||
is (currentUIState.webconsoleHeight, 0, "Web console is collapsed after toggling");
|
||||
ok (!currentUIState.openedConsolePanel, "The console panel is not the current tool");
|
||||
ok (!currentUIState.buttonSelected, "The command button is not selected.");
|
||||
}
|
||||
|
||||
|
||||
function testBottomHost()
|
||||
{
|
||||
checkHostType(Toolbox.HostType.BOTTOM);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: 0.8.681
|
||||
Current extension version is: 0.8.759
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
/* jshint esnext:true */
|
||||
/* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils,
|
||||
dump, NetworkManager, PdfJsTelemetry */
|
||||
dump, NetworkManager, PdfJsTelemetry, DEFAULT_PREFERENCES */
|
||||
|
||||
'use strict';
|
||||
|
||||
|
@ -33,12 +33,16 @@ const PDF_CONTENT_TYPE = 'application/pdf';
|
|||
const PREF_PREFIX = 'pdfjs';
|
||||
const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
|
||||
const MAX_DATABASE_LENGTH = 4096;
|
||||
const MAX_STRING_PREF_LENGTH = 4096;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/NetUtil.jsm');
|
||||
Cu.import('resource://pdf.js/network.js');
|
||||
|
||||
// Load the default preferences.
|
||||
Cu.import('resource://pdf.js/default_preferences.js');
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
|
||||
'resource://gre/modules/PrivateBrowsingUtils.jsm');
|
||||
|
||||
|
@ -58,6 +62,10 @@ function getChromeWindow(domWindow) {
|
|||
return containingBrowser.ownerDocument.defaultView;
|
||||
}
|
||||
|
||||
function setBoolPref(pref, value) {
|
||||
Services.prefs.setBoolPref(pref, value);
|
||||
}
|
||||
|
||||
function getBoolPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getBoolPref(pref);
|
||||
|
@ -66,6 +74,10 @@ function getBoolPref(pref, def) {
|
|||
}
|
||||
}
|
||||
|
||||
function setIntPref(pref, value) {
|
||||
Services.prefs.setIntPref(pref, value);
|
||||
}
|
||||
|
||||
function getIntPref(pref, def) {
|
||||
try {
|
||||
return Services.prefs.getIntPref(pref);
|
||||
|
@ -431,6 +443,62 @@ ChromeActions.prototype = {
|
|||
}
|
||||
getChromeWindow(this.domWindow).gFindBar
|
||||
.updateControlState(result, findPrevious);
|
||||
},
|
||||
setPreferences: function(prefs) {
|
||||
var prefValue, defaultValue, prefName, prefType, defaultType;
|
||||
|
||||
for (var key in DEFAULT_PREFERENCES) {
|
||||
prefValue = prefs[key];
|
||||
defaultValue = DEFAULT_PREFERENCES[key];
|
||||
prefName = (PREF_PREFIX + '.' + key);
|
||||
|
||||
if (prefValue === undefined || prefValue === defaultValue) {
|
||||
Services.prefs.clearUserPref(prefName);
|
||||
} else {
|
||||
prefType = typeof prefValue;
|
||||
defaultType = typeof defaultValue;
|
||||
|
||||
if (prefType !== defaultType) {
|
||||
continue;
|
||||
}
|
||||
switch (defaultType) {
|
||||
case 'boolean':
|
||||
setBoolPref(prefName, prefValue);
|
||||
break;
|
||||
case 'number':
|
||||
setIntPref(prefName, prefValue);
|
||||
break;
|
||||
case 'string':
|
||||
// Protect against adding arbitrarily long strings in about:config.
|
||||
if (prefValue.length <= MAX_STRING_PREF_LENGTH) {
|
||||
setStringPref(prefName, prefValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getPreferences: function() {
|
||||
var currentPrefs = {};
|
||||
var defaultValue, prefName;
|
||||
|
||||
for (var key in DEFAULT_PREFERENCES) {
|
||||
defaultValue = DEFAULT_PREFERENCES[key];
|
||||
prefName = (PREF_PREFIX + '.' + key);
|
||||
|
||||
switch (typeof defaultValue) {
|
||||
case 'boolean':
|
||||
currentPrefs[key] = getBoolPref(prefName, defaultValue);
|
||||
break;
|
||||
case 'number':
|
||||
currentPrefs[key] = getIntPref(prefName, defaultValue);
|
||||
break;
|
||||
case 'string':
|
||||
currentPrefs[key] = getStringPref(prefName, defaultValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(currentPrefs);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -439,9 +507,12 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||
* This is for range requests
|
||||
*/
|
||||
function RangedChromeActions(
|
||||
domWindow, contentDispositionFilename, originalRequest) {
|
||||
domWindow, contentDispositionFilename, originalRequest,
|
||||
dataListener) {
|
||||
|
||||
ChromeActions.call(this, domWindow, contentDispositionFilename);
|
||||
this.dataListener = dataListener;
|
||||
this.originalRequest = originalRequest;
|
||||
|
||||
this.pdfUrl = originalRequest.URI.spec;
|
||||
this.contentLength = originalRequest.contentLength;
|
||||
|
@ -487,11 +558,15 @@ var RangedChromeActions = (function RangedChromeActionsClosure() {
|
|||
proto.constructor = RangedChromeActions;
|
||||
|
||||
proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() {
|
||||
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
this.originalRequest = null;
|
||||
this.domWindow.postMessage({
|
||||
pdfjsLoadAction: 'supportsRangedLoading',
|
||||
pdfUrl: this.pdfUrl,
|
||||
length: this.contentLength
|
||||
length: this.contentLength,
|
||||
data: this.dataListener.getData()
|
||||
}, '*');
|
||||
this.dataListener = null;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
@ -692,8 +767,8 @@ PdfStreamConverter.prototype = {
|
|||
* 1. asyncConvertData stores the listener
|
||||
* 2. onStartRequest creates a new channel, streams the viewer
|
||||
* 3. If range requests are supported:
|
||||
* 3.1. Suspends and cancels the request so we can issue range
|
||||
* requests instead.
|
||||
* 3.1. Leave the request open until the viewer is ready to switch to
|
||||
* range requests.
|
||||
*
|
||||
* If range rquests are not supported:
|
||||
* 3.1. Read the stream as it's loaded in onDataAvailable to send
|
||||
|
@ -779,18 +854,12 @@ PdfStreamConverter.prototype = {
|
|||
PdfJsTelemetry.onViewerIsUsed();
|
||||
PdfJsTelemetry.onDocumentSize(aRequest.contentLength);
|
||||
|
||||
if (!rangeRequest) {
|
||||
|
||||
// Creating storage for PDF data
|
||||
var contentLength = aRequest.contentLength;
|
||||
this.dataListener = new PdfDataListener(contentLength);
|
||||
this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
|
||||
.createInstance(Ci.nsIBinaryInputStream);
|
||||
} else {
|
||||
// Suspend the request so we're not consuming any of the stream,
|
||||
// but we can't cancel the request yet. Otherwise, the original
|
||||
// listener will think we do not want to go the new PDF url
|
||||
aRequest.suspend();
|
||||
}
|
||||
|
||||
// Create a new channel that is viewer loaded as a resource.
|
||||
var ioService = Services.io;
|
||||
|
@ -816,12 +885,8 @@ PdfStreamConverter.prototype = {
|
|||
var domWindow = getDOMWindow(channel);
|
||||
var actions;
|
||||
if (rangeRequest) {
|
||||
// We are going to be issuing range requests, so cancel the
|
||||
// original request
|
||||
aRequest.resume();
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
actions = new RangedChromeActions(domWindow,
|
||||
contentDispositionFilename, aRequest);
|
||||
actions = new RangedChromeActions(
|
||||
domWindow, contentDispositionFilename, aRequest, dataListener);
|
||||
} else {
|
||||
actions = new StandardChromeActions(
|
||||
domWindow, contentDispositionFilename, dataListener);
|
||||
|
|
|
@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') {
|
|||
(typeof window !== 'undefined' ? window : this).PDFJS = {};
|
||||
}
|
||||
|
||||
PDFJS.version = '0.8.681';
|
||||
PDFJS.build = '48c672b';
|
||||
PDFJS.version = '0.8.759';
|
||||
PDFJS.build = 'd3b5aa3';
|
||||
|
||||
(function pdfjsWrapper() {
|
||||
// Use strict in our context only - users might not want it
|
||||
|
@ -43,7 +43,7 @@ PDFJS.build = '48c672b';
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref */
|
||||
/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */
|
||||
|
||||
'use strict';
|
||||
|
||||
|
@ -78,6 +78,98 @@ if (!globalScope.PDFJS) {
|
|||
|
||||
globalScope.PDFJS.pdfBug = false;
|
||||
|
||||
// All the possible operations for an operator list.
|
||||
var OPS = PDFJS.OPS = {
|
||||
// Intentionally start from 1 so it is easy to spot bad operators that will be
|
||||
// 0's.
|
||||
dependency: 1,
|
||||
setLineWidth: 2,
|
||||
setLineCap: 3,
|
||||
setLineJoin: 4,
|
||||
setMiterLimit: 5,
|
||||
setDash: 6,
|
||||
setRenderingIntent: 7,
|
||||
setFlatness: 8,
|
||||
setGState: 9,
|
||||
save: 10,
|
||||
restore: 11,
|
||||
transform: 12,
|
||||
moveTo: 13,
|
||||
lineTo: 14,
|
||||
curveTo: 15,
|
||||
curveTo2: 16,
|
||||
curveTo3: 17,
|
||||
closePath: 18,
|
||||
rectangle: 19,
|
||||
stroke: 20,
|
||||
closeStroke: 21,
|
||||
fill: 22,
|
||||
eoFill: 23,
|
||||
fillStroke: 24,
|
||||
eoFillStroke: 25,
|
||||
closeFillStroke: 26,
|
||||
closeEOFillStroke: 27,
|
||||
endPath: 28,
|
||||
clip: 29,
|
||||
eoClip: 30,
|
||||
beginText: 31,
|
||||
endText: 32,
|
||||
setCharSpacing: 33,
|
||||
setWordSpacing: 34,
|
||||
setHScale: 35,
|
||||
setLeading: 36,
|
||||
setFont: 37,
|
||||
setTextRenderingMode: 38,
|
||||
setTextRise: 39,
|
||||
moveText: 40,
|
||||
setLeadingMoveText: 41,
|
||||
setTextMatrix: 42,
|
||||
nextLine: 43,
|
||||
showText: 44,
|
||||
showSpacedText: 45,
|
||||
nextLineShowText: 46,
|
||||
nextLineSetSpacingShowText: 47,
|
||||
setCharWidth: 48,
|
||||
setCharWidthAndBounds: 49,
|
||||
setStrokeColorSpace: 50,
|
||||
setFillColorSpace: 51,
|
||||
setStrokeColor: 52,
|
||||
setStrokeColorN: 53,
|
||||
setFillColor: 54,
|
||||
setFillColorN: 55,
|
||||
setStrokeGray: 56,
|
||||
setFillGray: 57,
|
||||
setStrokeRGBColor: 58,
|
||||
setFillRGBColor: 59,
|
||||
setStrokeCMYKColor: 60,
|
||||
setFillCMYKColor: 61,
|
||||
shadingFill: 62,
|
||||
beginInlineImage: 63,
|
||||
beginImageData: 64,
|
||||
endInlineImage: 65,
|
||||
paintXObject: 66,
|
||||
markPoint: 67,
|
||||
markPointProps: 68,
|
||||
beginMarkedContent: 69,
|
||||
beginMarkedContentProps: 70,
|
||||
endMarkedContent: 71,
|
||||
beginCompat: 72,
|
||||
endCompat: 73,
|
||||
paintFormXObjectBegin: 74,
|
||||
paintFormXObjectEnd: 75,
|
||||
beginGroup: 76,
|
||||
endGroup: 77,
|
||||
beginAnnotations: 78,
|
||||
endAnnotations: 79,
|
||||
beginAnnotation: 80,
|
||||
endAnnotation: 81,
|
||||
paintJpegXObject: 82,
|
||||
paintImageMaskXObject: 83,
|
||||
paintImageMaskXObjectGroup: 84,
|
||||
paintImageXObject: 85,
|
||||
paintInlineImageXObject: 86,
|
||||
paintInlineImageXObjectGroup: 87
|
||||
};
|
||||
|
||||
// Use only for debugging purposes. This should not be used in any code that is
|
||||
// in mozilla master.
|
||||
|
@ -292,7 +384,7 @@ var MissingDataException = (function MissingDataExceptionClosure() {
|
|||
function MissingDataException(begin, end) {
|
||||
this.begin = begin;
|
||||
this.end = end;
|
||||
this.message = 'Missing data [begin, end)';
|
||||
this.message = 'Missing data [' + begin + ', ' + end + ')';
|
||||
}
|
||||
|
||||
MissingDataException.prototype = new Error();
|
||||
|
@ -865,7 +957,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
|||
/**
|
||||
* Builds a promise that is resolved when all the passed in promises are
|
||||
* resolved.
|
||||
* @param {Promise[]} promises Array of promises to wait for.
|
||||
* @param {array} array of data and/or promises to wait for.
|
||||
* @return {Promise} New dependant promise.
|
||||
*/
|
||||
Promise.all = function Promise_all(promises) {
|
||||
|
@ -885,7 +977,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
|||
}
|
||||
for (var i = 0, ii = promises.length; i < ii; ++i) {
|
||||
var promise = promises[i];
|
||||
promise.then((function(i) {
|
||||
var resolve = (function(i) {
|
||||
return function(value) {
|
||||
if (deferred._status === STATUS_REJECTED) {
|
||||
return;
|
||||
|
@ -895,11 +987,24 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
|||
if (unresolved === 0)
|
||||
deferred.resolve(results);
|
||||
};
|
||||
})(i), reject);
|
||||
})(i);
|
||||
if (Promise.isPromise(promise)) {
|
||||
promise.then(resolve, reject);
|
||||
} else {
|
||||
resolve(promise);
|
||||
}
|
||||
}
|
||||
return deferred;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the value is likely a promise (has a 'then' function).
|
||||
* @return {boolean} true if x is thenable
|
||||
*/
|
||||
Promise.isPromise = function Promise_isPromise(value) {
|
||||
return value && typeof value.then === 'function';
|
||||
};
|
||||
|
||||
Promise.prototype = {
|
||||
_status: null,
|
||||
_value: null,
|
||||
|
@ -913,7 +1018,7 @@ var Promise = PDFJS.Promise = (function PromiseClosure() {
|
|||
}
|
||||
|
||||
if (status == STATUS_RESOLVED &&
|
||||
value && typeof(value.then) === 'function') {
|
||||
Promise.isPromise(value)) {
|
||||
value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
|
||||
this._updateStatus.bind(this, STATUS_REJECTED));
|
||||
return;
|
||||
|
@ -1016,7 +1121,7 @@ var StatTimer = (function StatTimerClosure() {
|
|||
})();
|
||||
|
||||
PDFJS.createBlob = function createBlob(data, contentType) {
|
||||
if (typeof Blob === 'function')
|
||||
if (typeof Blob !== 'undefined')
|
||||
return new Blob([data], { type: contentType });
|
||||
// Blob builder is deprecated in FF14 and removed in FF18.
|
||||
var bb = new MozBlobBuilder();
|
||||
|
@ -1024,10 +1129,38 @@ PDFJS.createBlob = function createBlob(data, contentType) {
|
|||
return bb.getBlob(contentType);
|
||||
};
|
||||
|
||||
PDFJS.createObjectURL = (function createObjectURLClosure() {
|
||||
if (typeof URL !== 'undefined' && URL.createObjectURL) {
|
||||
return function createObjectURL(data, contentType) {
|
||||
var blob = PDFJS.createBlob(data, contentType);
|
||||
return URL.createObjectURL(blob);
|
||||
};
|
||||
}
|
||||
|
||||
// Blob/createObjectURL is not available, falling back to data schema.
|
||||
var digits =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
return function createObjectURL(data, contentType) {
|
||||
var buffer = 'data:' + contentType + ';base64,';
|
||||
for (var i = 0, ii = data.length; i < ii; i += 3) {
|
||||
var b1 = data[i] & 0xFF;
|
||||
var b2 = data[i + 1] & 0xFF;
|
||||
var b3 = data[i + 2] & 0xFF;
|
||||
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
||||
var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
|
||||
var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
|
||||
buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
|
||||
}
|
||||
return buffer;
|
||||
};
|
||||
})();
|
||||
|
||||
function MessageHandler(name, comObj) {
|
||||
this.name = name;
|
||||
this.comObj = comObj;
|
||||
this.callbackIndex = 1;
|
||||
this.postMessageTransfers = true;
|
||||
var callbacks = this.callbacks = {};
|
||||
var ah = this.actionHandler = {};
|
||||
|
||||
|
@ -1094,8 +1227,9 @@ MessageHandler.prototype = {
|
|||
* @param {String} actionName Action to call.
|
||||
* @param {JSON} data JSON data to send.
|
||||
* @param {function} [callback] Optional callback that will handle a reply.
|
||||
* @param {Array} [transfers] Optional list of transfers/ArrayBuffers
|
||||
*/
|
||||
send: function messageHandlerSend(actionName, data, callback) {
|
||||
send: function messageHandlerSend(actionName, data, callback, transfers) {
|
||||
var message = {
|
||||
action: actionName,
|
||||
data: data
|
||||
|
@ -1105,16 +1239,20 @@ MessageHandler.prototype = {
|
|||
this.callbacks[callbackId] = callback;
|
||||
message.callbackId = callbackId;
|
||||
}
|
||||
if (transfers && this.postMessageTransfers) {
|
||||
this.comObj.postMessage(message, transfers);
|
||||
} else {
|
||||
this.comObj.postMessage(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function loadJpegStream(id, imageData, objs) {
|
||||
function loadJpegStream(id, imageUrl, objs) {
|
||||
var img = new Image();
|
||||
img.onload = (function loadJpegStream_onloadClosure() {
|
||||
objs.resolve(id, img);
|
||||
});
|
||||
img.src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
img.src = imageUrl;
|
||||
}
|
||||
|
||||
|
||||
|
@ -3430,9 +3568,9 @@ var Annotation = (function AnnotationClosure() {
|
|||
|
||||
resourcesPromise.then(function(resources) {
|
||||
var opList = new OperatorList();
|
||||
opList.addOp('beginAnnotation', [data.rect, transform, matrix]);
|
||||
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
|
||||
evaluator.getOperatorList(this.appearance, resources, opList);
|
||||
opList.addOp('endAnnotation', []);
|
||||
opList.addOp(OPS.endAnnotation, []);
|
||||
promise.resolve(opList);
|
||||
}.bind(this));
|
||||
|
||||
|
@ -3526,12 +3664,12 @@ var Annotation = (function AnnotationClosure() {
|
|||
annotationPromises.push(annotations[i].getOperatorList(partialEvaluator));
|
||||
}
|
||||
Promise.all(annotationPromises).then(function(datas) {
|
||||
opList.addOp('beginAnnotations', []);
|
||||
opList.addOp(OPS.beginAnnotations, []);
|
||||
for (var i = 0, n = datas.length; i < n; ++i) {
|
||||
var annotOpList = datas[i];
|
||||
opList.addOpList(annotOpList);
|
||||
}
|
||||
opList.addOp('endAnnotations', []);
|
||||
opList.addOp(OPS.endAnnotations, []);
|
||||
annotationsReadyPromise.resolve();
|
||||
}, reject);
|
||||
|
||||
|
@ -3707,10 +3845,10 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
|||
data.rgb = [0, 0, 0];
|
||||
// TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY!
|
||||
for (var i = 0, n = fnArray.length; i < n; ++i) {
|
||||
var fnName = appearanceFnArray[i];
|
||||
var fnId = appearanceFnArray[i];
|
||||
var args = appearanceArgsArray[i];
|
||||
|
||||
if (fnName === 'setFont') {
|
||||
if (fnId === OPS.setFont) {
|
||||
data.fontRefName = args[0];
|
||||
var size = args[1];
|
||||
if (size < 0) {
|
||||
|
@ -3720,9 +3858,9 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
|
|||
data.fontDirection = 1;
|
||||
data.fontSize = size;
|
||||
}
|
||||
} else if (fnName === 'setFillRGBColor') {
|
||||
} else if (fnId === OPS.setFillRGBColor) {
|
||||
data.rgb = args;
|
||||
} else if (fnName === 'setFillGray') {
|
||||
} else if (fnId === OPS.setFillGray) {
|
||||
var rgbValue = args[0] * 255;
|
||||
data.rgb = [rgbValue, rgbValue, rgbValue];
|
||||
}
|
||||
|
@ -3973,7 +4111,9 @@ PDFJS.disableWorker = PDFJS.disableWorker === undefined ?
|
|||
false : PDFJS.disableWorker;
|
||||
|
||||
/**
|
||||
* Path and filename of the worker file. Required when the worker is enabled.
|
||||
* Path and filename of the worker file. Required when the worker is enabled in
|
||||
* development mode. If unspecified in the production build, the worker will be
|
||||
* loaded based on the location of the pdf.js file.
|
||||
* @var {String}
|
||||
*/
|
||||
PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc;
|
||||
|
@ -4002,6 +4142,12 @@ PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ?
|
|||
*/
|
||||
PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
|
||||
|
||||
/**
|
||||
* Enables transfer usage in postMessage for ArrayBuffers.
|
||||
* @var {boolean}
|
||||
*/
|
||||
PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ?
|
||||
true : PDFJS.postMessageTransfers;
|
||||
/**
|
||||
* This is the main entry point for loading a PDF and interacting with it.
|
||||
* NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR)
|
||||
|
@ -4015,6 +4161,9 @@ PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug;
|
|||
* - data - A typed array with PDF data.
|
||||
* - httpHeaders - Basic authentication headers.
|
||||
* - password - For decrypting password-protected PDFs.
|
||||
* - initialData - A typed array with the first portion or all of the pdf data.
|
||||
* Used by the extension since some data is already loaded
|
||||
* before the switch to range requests.
|
||||
*
|
||||
* @param {object} pdfDataRangeTransport is optional. It is used if you want
|
||||
* to manually serve range requests for data in the PDF. See viewer.js for
|
||||
|
@ -4104,6 +4253,14 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
|||
getPage: function PDFDocumentProxy_getPage(number) {
|
||||
return this.transport.getPage(number);
|
||||
},
|
||||
/**
|
||||
* @param {object} Must have 'num' and 'gen' properties.
|
||||
* @return {Promise} A promise that is resolved with the page index that is
|
||||
* associated with the reference.
|
||||
*/
|
||||
getPageIndex: function PDFDocumentProxy_getPageIndex(ref) {
|
||||
return this.transport.getPageIndex(ref);
|
||||
},
|
||||
/**
|
||||
* @return {Promise} A promise that is resolved with a lookup table for
|
||||
* mapping named destinations to reference numbers.
|
||||
|
@ -4179,6 +4336,9 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() {
|
|||
dataLoaded: function PDFDocumentProxy_dataLoaded() {
|
||||
return this.transport.dataLoaded();
|
||||
},
|
||||
cleanup: function PDFDocumentProxy_cleanup() {
|
||||
this.transport.startCleanup();
|
||||
},
|
||||
destroy: function PDFDocumentProxy_destroy() {
|
||||
this.transport.destroy();
|
||||
}
|
||||
|
@ -4398,10 +4558,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() {
|
|||
*/
|
||||
_renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) {
|
||||
// Add the new chunk to the current operator list.
|
||||
Util.concatenateToArray(this.operatorList.fnArray,
|
||||
operatorListChunk.fnArray);
|
||||
Util.concatenateToArray(this.operatorList.argsArray,
|
||||
operatorListChunk.argsArray);
|
||||
for (var i = 0, ii = operatorListChunk.length; i < ii; i++) {
|
||||
this.operatorList.fnArray.push(operatorListChunk.fnArray[i]);
|
||||
this.operatorList.argsArray.push(operatorListChunk.argsArray[i]);
|
||||
}
|
||||
this.operatorList.lastChunk = operatorListChunk.lastChunk;
|
||||
|
||||
// Notify all the rendering tasks there are more operators to be consumed.
|
||||
|
@ -4453,9 +4613,13 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
var messageHandler = new MessageHandler('main', worker);
|
||||
this.messageHandler = messageHandler;
|
||||
|
||||
messageHandler.on('test', function transportTest(supportTypedArray) {
|
||||
messageHandler.on('test', function transportTest(data) {
|
||||
var supportTypedArray = data && data.supportTypedArray;
|
||||
if (supportTypedArray) {
|
||||
this.worker = worker;
|
||||
if (!data.supportTransfers) {
|
||||
PDFJS.postMessageTransfers = false;
|
||||
}
|
||||
this.setupMessageHandler(messageHandler);
|
||||
workerInitializedPromise.resolve();
|
||||
} else {
|
||||
|
@ -4467,10 +4631,16 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
}
|
||||
}.bind(this));
|
||||
|
||||
var testObj = new Uint8Array(1);
|
||||
// Some versions of Opera throw a DATA_CLONE_ERR on
|
||||
// serializing the typed array.
|
||||
var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]);
|
||||
// Some versions of Opera throw a DATA_CLONE_ERR on serializing the
|
||||
// typed array. Also, checking if we can use transfers.
|
||||
try {
|
||||
messageHandler.send('test', testObj, null, [testObj.buffer]);
|
||||
} catch (ex) {
|
||||
info('Cannot use postMessage transfers');
|
||||
testObj[0] = 0;
|
||||
messageHandler.send('test', testObj);
|
||||
}
|
||||
return;
|
||||
} catch (e) {
|
||||
info('The worker has been disabled.');
|
||||
|
@ -4704,7 +4874,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
}, this);
|
||||
|
||||
messageHandler.on('JpegDecode', function(data, promise) {
|
||||
var imageData = data[0];
|
||||
var imageUrl = data[0];
|
||||
var components = data[1];
|
||||
if (components != 3 && components != 1)
|
||||
error('Only 3 component or 1 component can be returned');
|
||||
|
@ -4734,8 +4904,7 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
}
|
||||
promise.resolve({ data: buf, width: width, height: height});
|
||||
}).bind(this);
|
||||
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
img.src = src;
|
||||
img.src = imageUrl;
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -4774,6 +4943,16 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
return promise;
|
||||
},
|
||||
|
||||
getPageIndex: function WorkerTransport_getPageIndexByRef(ref) {
|
||||
var promise = new PDFJS.Promise();
|
||||
this.messageHandler.send('GetPageIndex', { ref: ref },
|
||||
function (pageIndex) {
|
||||
promise.resolve(pageIndex);
|
||||
}
|
||||
);
|
||||
return promise;
|
||||
},
|
||||
|
||||
getAnnotations: function WorkerTransport_getAnnotations(pageIndex) {
|
||||
this.messageHandler.send('GetAnnotationsRequest',
|
||||
{ pageIndex: pageIndex });
|
||||
|
@ -4787,6 +4966,21 @@ var WorkerTransport = (function WorkerTransportClosure() {
|
|||
}
|
||||
);
|
||||
return promise;
|
||||
},
|
||||
|
||||
startCleanup: function WorkerTransport_startCleanup() {
|
||||
this.messageHandler.send('Cleanup', null,
|
||||
function endCleanup() {
|
||||
for (var i = 0, ii = this.pageCache.length; i < ii; i++) {
|
||||
var page = this.pageCache[i];
|
||||
if (page) {
|
||||
page.destroy();
|
||||
}
|
||||
}
|
||||
this.commonObjs.clear();
|
||||
FontLoader.clear();
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
};
|
||||
return WorkerTransport;
|
||||
|
@ -5007,7 +5201,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() {
|
|||
this.operatorListIdx,
|
||||
this._continue.bind(this),
|
||||
this.stepper);
|
||||
if (this.operatorListIdx === this.operatorList.fnArray.length) {
|
||||
if (this.operatorListIdx === this.operatorList.argsArray.length) {
|
||||
this.running = false;
|
||||
if (this.operatorList.lastChunk) {
|
||||
this.gfx.endDrawing();
|
||||
|
@ -5536,37 +5730,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
var EO_CLIP = {};
|
||||
|
||||
CanvasGraphics.prototype = {
|
||||
slowCommands: {
|
||||
'stroke': true,
|
||||
'closeStroke': true,
|
||||
'fill': true,
|
||||
'eoFill': true,
|
||||
'fillStroke': true,
|
||||
'eoFillStroke': true,
|
||||
'closeFillStroke': true,
|
||||
'closeEOFillStroke': true,
|
||||
'showText': true,
|
||||
'showSpacedText': true,
|
||||
'setStrokeColorSpace': true,
|
||||
'setFillColorSpace': true,
|
||||
'setStrokeColor': true,
|
||||
'setStrokeColorN': true,
|
||||
'setFillColor': true,
|
||||
'setFillColorN': true,
|
||||
'setStrokeGray': true,
|
||||
'setFillGray': true,
|
||||
'setStrokeRGBColor': true,
|
||||
'setFillRGBColor': true,
|
||||
'setStrokeCMYKColor': true,
|
||||
'setFillCMYKColor': true,
|
||||
'paintJpegXObject': true,
|
||||
'paintImageXObject': true,
|
||||
'paintInlineImageXObject': true,
|
||||
'paintInlineImageXObjectGroup': true,
|
||||
'paintImageMaskXObject': true,
|
||||
'paintImageMaskXObjectGroup': true,
|
||||
'shadingFill': true
|
||||
},
|
||||
|
||||
beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
|
||||
// For pdfs that use blend modes we have to clear the canvas else certain
|
||||
|
@ -5618,8 +5781,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
|
||||
var commonObjs = this.commonObjs;
|
||||
var objs = this.objs;
|
||||
var fnName;
|
||||
var slowCommands = this.slowCommands;
|
||||
var fnId;
|
||||
|
||||
while (true) {
|
||||
if (stepper && i === stepper.nextBreakPoint) {
|
||||
|
@ -5627,10 +5789,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
return i;
|
||||
}
|
||||
|
||||
fnName = fnArray[i];
|
||||
fnId = fnArray[i];
|
||||
|
||||
if (fnName !== 'dependency') {
|
||||
this[fnName].apply(this, argsArray[i]);
|
||||
if (fnId !== OPS.dependency) {
|
||||
this[fnId].apply(this, argsArray[i]);
|
||||
} else {
|
||||
var deps = argsArray[i];
|
||||
for (var n = 0, nn = deps.length; n < nn; n++) {
|
||||
|
@ -5657,10 +5819,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
return i;
|
||||
}
|
||||
|
||||
// If the execution took longer then a certain amount of time, shedule
|
||||
// If the execution took longer then a certain amount of time, schedule
|
||||
// to continue exeution after a short delay.
|
||||
// However, this is only possible if a 'continueCallback' is passed in.
|
||||
if (continueCallback && slowCommands[fnName] && Date.now() > endTime) {
|
||||
if (continueCallback && Date.now() > endTime) {
|
||||
setTimeout(continueCallback, 0);
|
||||
return i;
|
||||
}
|
||||
|
@ -6947,6 +7109,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
}
|
||||
};
|
||||
|
||||
for (var op in OPS) {
|
||||
CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op];
|
||||
}
|
||||
|
||||
return CanvasGraphics;
|
||||
})();
|
||||
|
||||
|
@ -6967,6 +7133,12 @@ var FontLoader = {
|
|||
var styleSheet = styleElement.sheet;
|
||||
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
||||
},
|
||||
clear: function fontLoaderClear() {
|
||||
var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
|
||||
if (styleElement) {
|
||||
styleElement.parentNode.removeChild(styleElement);
|
||||
}
|
||||
},
|
||||
bind: function fontLoaderBind(fonts, callback) {
|
||||
assert(!isWorker, 'bind() shall be called from main thread');
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
/* Copyright 2013 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* globals */
|
||||
|
||||
'use strict';
|
||||
|
||||
var EXPORTED_SYMBOLS = ['DEFAULT_PREFERENCES'];
|
||||
|
||||
var DEFAULT_PREFERENCES = {
|
||||
showPreviousViewOnLoad: true,
|
||||
defaultZoomValue: '',
|
||||
ifAvailableShowOutlineOnLoad: false
|
||||
};
|
|
@ -240,6 +240,8 @@ var Stepper = (function StepperClosure() {
|
|||
return out;
|
||||
}
|
||||
|
||||
var opMap = null;
|
||||
|
||||
var glyphCommands = {
|
||||
'showText': 0,
|
||||
'showSpacedText': 0,
|
||||
|
@ -271,6 +273,12 @@ var Stepper = (function StepperClosure() {
|
|||
headerRow.appendChild(c('th', 'args'));
|
||||
panel.appendChild(content);
|
||||
this.table = table;
|
||||
if (!opMap) {
|
||||
opMap = Object.create(null);
|
||||
for (var key in PDFJS.OPS) {
|
||||
opMap[PDFJS.OPS[key]] = key;
|
||||
}
|
||||
}
|
||||
},
|
||||
updateOperatorList: function updateOperatorList(operatorList) {
|
||||
var self = this;
|
||||
|
@ -300,7 +308,7 @@ var Stepper = (function StepperClosure() {
|
|||
breakCell.appendChild(cbox);
|
||||
line.appendChild(breakCell);
|
||||
line.appendChild(c('td', i.toString()));
|
||||
var fn = operatorList.fnArray[i];
|
||||
var fn = opMap[operatorList.fnArray[i]];
|
||||
var decArgs = args;
|
||||
if (fn in glyphCommands) {
|
||||
var glyphIndex = glyphCommands[fn];
|
||||
|
|
|
@ -144,7 +144,6 @@ limitations under the License.
|
|||
</div>
|
||||
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
|
||||
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="8">
|
||||
</input>
|
||||
<span id="numPages" class="toolbarLabel"></span>
|
||||
</div>
|
||||
<div id="toolbarViewerRight">
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
*/
|
||||
/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
|
||||
PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
|
||||
getFileName, getOutputScale, scrollIntoView, getPDFFileNameFromURL,
|
||||
PDFHistory, Settings, PageView, ThumbnailView, noContextMenuHandler,
|
||||
getFileName, scrollIntoView, getPDFFileNameFromURL, PDFHistory,
|
||||
Preferences, Settings, PageView, ThumbnailView, noContextMenuHandler,
|
||||
SecondaryToolbar, PasswordPrompt, PresentationMode */
|
||||
|
||||
'use strict';
|
||||
|
@ -38,6 +38,7 @@ var SCALE_SELECT_CONTAINER_PADDING = 8;
|
|||
var SCALE_SELECT_PADDING = 22;
|
||||
var THUMBNAIL_SCROLL_MARGIN = -19;
|
||||
var USE_ONLY_CSS_ZOOM = false;
|
||||
var CLEANUP_TIMEOUT = 30000;
|
||||
var RenderingStates = {
|
||||
INITIAL: 0,
|
||||
RUNNING: 1,
|
||||
|
@ -154,6 +155,9 @@ function scrollIntoView(element, spot) {
|
|||
return;
|
||||
}
|
||||
while (parent.clientHeight == parent.scrollHeight) {
|
||||
if (parent.dataset._scaleY) {
|
||||
offsetY /= parent.dataset._scaleY;
|
||||
}
|
||||
offsetY += parent.offsetTop;
|
||||
parent = parent.offsetParent;
|
||||
if (!parent)
|
||||
|
@ -284,6 +288,92 @@ var Cache = function cacheCache(size) {
|
|||
|
||||
|
||||
|
||||
var EXPORTED_SYMBOLS = ['DEFAULT_PREFERENCES'];
|
||||
|
||||
var DEFAULT_PREFERENCES = {
|
||||
showPreviousViewOnLoad: true,
|
||||
defaultZoomValue: '',
|
||||
ifAvailableShowOutlineOnLoad: false
|
||||
};
|
||||
|
||||
|
||||
var Preferences = (function PreferencesClosure() {
|
||||
function Preferences() {
|
||||
this.prefs = {};
|
||||
this.initializedPromise = this.readFromStorage().then(function(prefObj) {
|
||||
if (prefObj) {
|
||||
this.prefs = prefObj;
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
Preferences.prototype = {
|
||||
writeToStorage: function Preferences_writeToStorage(prefObj) {
|
||||
return;
|
||||
},
|
||||
|
||||
readFromStorage: function Preferences_readFromStorage() {
|
||||
var readFromStoragePromise = new PDFJS.Promise();
|
||||
return readFromStoragePromise;
|
||||
},
|
||||
|
||||
reset: function Preferences_reset() {
|
||||
if (this.initializedPromise.isResolved) {
|
||||
this.prefs = {};
|
||||
this.writeToStorage(this.prefs);
|
||||
}
|
||||
},
|
||||
|
||||
set: function Preferences_set(name, value) {
|
||||
if (!this.initializedPromise.isResolved) {
|
||||
return;
|
||||
} else if (DEFAULT_PREFERENCES[name] === undefined) {
|
||||
console.error('Preferences_set: \'' + name + '\' is undefined.');
|
||||
return;
|
||||
} else if (value === undefined) {
|
||||
console.error('Preferences_set: no value is specified.');
|
||||
return;
|
||||
}
|
||||
var valueType = typeof value;
|
||||
var defaultType = typeof DEFAULT_PREFERENCES[name];
|
||||
|
||||
if (valueType !== defaultType) {
|
||||
if (valueType === 'number' && defaultType === 'string') {
|
||||
value = value.toString();
|
||||
} else {
|
||||
console.error('Preferences_set: \'' + value + '\' is a \"' +
|
||||
valueType + '\", expected a \"' + defaultType + '\".');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.prefs[name] = value;
|
||||
this.writeToStorage(this.prefs);
|
||||
},
|
||||
|
||||
get: function Preferences_get(name) {
|
||||
var defaultPref = DEFAULT_PREFERENCES[name];
|
||||
|
||||
if (defaultPref === undefined) {
|
||||
console.error('Preferences_get: \'' + name + '\' is undefined.');
|
||||
return;
|
||||
} else if (this.initializedPromise.isResolved) {
|
||||
var pref = this.prefs[name];
|
||||
|
||||
if (pref !== undefined) {
|
||||
return pref;
|
||||
}
|
||||
}
|
||||
return defaultPref;
|
||||
}
|
||||
};
|
||||
|
||||
return Preferences;
|
||||
})();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var FirefoxCom = (function FirefoxComClosure() {
|
||||
|
@ -373,6 +463,17 @@ var DownloadManager = (function DownloadManagerClosure() {
|
|||
return DownloadManager;
|
||||
})();
|
||||
|
||||
Preferences.prototype.writeToStorage = function(prefObj) {
|
||||
FirefoxCom.requestSync('setPreferences', prefObj);
|
||||
};
|
||||
|
||||
Preferences.prototype.readFromStorage = function() {
|
||||
var readFromStoragePromise = new PDFJS.Promise();
|
||||
var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences'));
|
||||
readFromStoragePromise.resolve(readPrefs);
|
||||
return readFromStoragePromise;
|
||||
};
|
||||
|
||||
|
||||
var cache = new Cache(CACHE_SIZE);
|
||||
var currentPageNumber = 1;
|
||||
|
@ -580,11 +681,12 @@ var PDFFindBar = {
|
|||
},
|
||||
|
||||
open: function() {
|
||||
if (this.opened) return;
|
||||
|
||||
if (!this.opened) {
|
||||
this.opened = true;
|
||||
this.toggleButton.classList.add('toggled');
|
||||
this.bar.classList.remove('hidden');
|
||||
}
|
||||
|
||||
this.findField.select();
|
||||
this.findField.focus();
|
||||
},
|
||||
|
@ -797,8 +899,8 @@ var PDFFindController = {
|
|||
},
|
||||
|
||||
nextMatch: function() {
|
||||
var pages = this.pdfPageSource.pages;
|
||||
var previous = this.state.findPrevious;
|
||||
var currentPageIndex = this.pdfPageSource.page - 1;
|
||||
var numPages = this.pdfPageSource.pages.length;
|
||||
|
||||
this.active = true;
|
||||
|
@ -807,7 +909,7 @@ var PDFFindController = {
|
|||
// Need to recalculate the matches, reset everything.
|
||||
this.dirtyMatch = false;
|
||||
this.selected.pageIdx = this.selected.matchIdx = -1;
|
||||
this.offset.pageIdx = previous ? numPages - 1 : 0;
|
||||
this.offset.pageIdx = currentPageIndex;
|
||||
this.offset.matchIdx = null;
|
||||
this.hadMatch = false;
|
||||
this.resumeCallback = null;
|
||||
|
@ -998,8 +1100,7 @@ var PDFHistory = {
|
|||
// is opened in the web viewer.
|
||||
this.reInitialized = true;
|
||||
}
|
||||
window.history.replaceState({ fingerprint: this.fingerprint }, '',
|
||||
document.URL);
|
||||
this._pushOrReplaceState({ fingerprint: this.fingerprint }, true);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
@ -1064,6 +1165,15 @@ var PDFHistory = {
|
|||
state.target && state.target.hash) ? true : false;
|
||||
},
|
||||
|
||||
_pushOrReplaceState: function pdfHistory_pushOrReplaceState(stateObj,
|
||||
replace) {
|
||||
if (replace) {
|
||||
window.history.replaceState(stateObj, '');
|
||||
} else {
|
||||
window.history.pushState(stateObj, '');
|
||||
}
|
||||
},
|
||||
|
||||
get isHashChangeUnlocked() {
|
||||
if (!this.initialized) {
|
||||
return true;
|
||||
|
@ -1224,11 +1334,8 @@ var PDFHistory = {
|
|||
this._pushToHistory(previousParams, false, replacePrevious);
|
||||
}
|
||||
}
|
||||
if (overwrite || this.uid === 0) {
|
||||
window.history.replaceState(this._stateObj(params), '', document.URL);
|
||||
} else {
|
||||
window.history.pushState(this._stateObj(params), '', document.URL);
|
||||
}
|
||||
this._pushOrReplaceState(this._stateObj(params),
|
||||
(overwrite || this.uid === 0));
|
||||
this.currentUid = this.uid++;
|
||||
this.current = params;
|
||||
this.updatePreviousBookmark = true;
|
||||
|
@ -1689,6 +1796,7 @@ var PDFView = {
|
|||
lastScroll: 0,
|
||||
previousPageNumber: 1,
|
||||
isViewerEmbedded: (window.parent !== window),
|
||||
idleTimeout: null,
|
||||
|
||||
// called once when the document is loaded
|
||||
initialize: function pdfViewInitialize() {
|
||||
|
@ -1789,7 +1897,7 @@ var PDFView = {
|
|||
var number = parseFloat(value);
|
||||
var scale;
|
||||
|
||||
if (number) {
|
||||
if (number > 0) {
|
||||
scale = number;
|
||||
resetAutoSettings = true;
|
||||
} else {
|
||||
|
@ -1816,6 +1924,10 @@ var PDFView = {
|
|||
case 'auto':
|
||||
scale = Math.min(MAX_AUTO_SCALE, pageWidthScale);
|
||||
break;
|
||||
default:
|
||||
console.error('pdfViewSetScale: \'' + value +
|
||||
'\' is an unknown zoom value.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.currentScaleValue = value;
|
||||
|
@ -2019,7 +2131,8 @@ var PDFView = {
|
|||
switch (args.pdfjsLoadAction) {
|
||||
case 'supportsRangedLoading':
|
||||
PDFView.open(args.pdfUrl, 0, undefined, pdfDataRangeTransport, {
|
||||
length: args.length
|
||||
length: args.length,
|
||||
initialData: args.data
|
||||
});
|
||||
break;
|
||||
case 'range':
|
||||
|
@ -2182,9 +2295,9 @@ var PDFView = {
|
|||
// Update the browsing history.
|
||||
PDFHistory.push({ dest: dest, hash: destString, page: pageNumber });
|
||||
} else {
|
||||
self.pendingRefStrLoaded = new PDFJS.Promise();
|
||||
self.pendingRefStr = destRef.num + ' ' + destRef.gen + ' R';
|
||||
self.pendingRefStrLoaded.then(function() {
|
||||
self.pdfDocument.getPageIndex(destRef).then(function (pageIndex) {
|
||||
var pageNum = pageIndex + 1;
|
||||
self.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageNum;
|
||||
goToDestination(destRef);
|
||||
});
|
||||
}
|
||||
|
@ -2289,9 +2402,14 @@ var PDFView = {
|
|||
},
|
||||
|
||||
load: function pdfViewLoad(pdfDocument, scale) {
|
||||
var self = this;
|
||||
var onePageRendered = new PDFJS.Promise();
|
||||
function bindOnAfterDraw(pageView, thumbnailView) {
|
||||
// when page is painted, using the image as thumbnail base
|
||||
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
||||
if (!onePageRendered.isResolved) {
|
||||
onePageRendered.resolve();
|
||||
}
|
||||
thumbnailView.setImage(pageView.canvas);
|
||||
};
|
||||
}
|
||||
|
@ -2329,6 +2447,7 @@ var PDFView = {
|
|||
mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
|
||||
document.getElementById('pageNumber').max = pagesCount;
|
||||
|
||||
var prefs = PDFView.prefs = new Preferences();
|
||||
PDFView.documentFingerprint = id;
|
||||
var store = PDFView.store = new Settings(id);
|
||||
|
||||
|
@ -2339,7 +2458,6 @@ var PDFView = {
|
|||
var thumbnails = this.thumbnails = [];
|
||||
|
||||
var pagesPromise = this.pagesPromise = new PDFJS.Promise();
|
||||
var self = this;
|
||||
|
||||
var firstPagePromise = pdfDocument.getPage(1);
|
||||
|
||||
|
@ -2347,7 +2465,6 @@ var PDFView = {
|
|||
// viewport for all pages
|
||||
firstPagePromise.then(function(pdfPage) {
|
||||
var viewport = pdfPage.getViewport((scale || 1.0) * CSS_UNITS);
|
||||
var pagePromises = [];
|
||||
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
||||
var viewportClone = viewport.clone();
|
||||
var pageView = new PageView(container, pageNum, scale,
|
||||
|
@ -2360,56 +2477,59 @@ var PDFView = {
|
|||
thumbnails.push(thumbnailView);
|
||||
}
|
||||
|
||||
// Fetch all the pages since the viewport is needed before printing
|
||||
// starts to create the correct size canvas. Wait until one page is
|
||||
// rendered so we don't tie up too many resources early on.
|
||||
onePageRendered.then(function () {
|
||||
if (!PDFJS.disableAutoFetch) {
|
||||
var getPagesLeft = pagesCount;
|
||||
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
||||
pdfDocument.getPage(pageNum).then(function (pageNum, pdfPage) {
|
||||
var pageView = pages[pageNum - 1];
|
||||
if (!pageView.pdfPage) {
|
||||
pageView.setPdfPage(pdfPage);
|
||||
}
|
||||
var refStr = pdfPage.ref.num + ' ' + pdfPage.ref.gen + ' R';
|
||||
pagesRefMap[refStr] = pageNum;
|
||||
getPagesLeft--;
|
||||
if (!getPagesLeft) {
|
||||
pagesPromise.resolve();
|
||||
}
|
||||
}.bind(null, pageNum));
|
||||
}
|
||||
} else {
|
||||
// XXX: Printing is semi-broken with auto fetch disabled.
|
||||
pagesPromise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('documentload', true, true, {});
|
||||
window.dispatchEvent(event);
|
||||
|
||||
PDFView.loadingBar.setWidth(container);
|
||||
|
||||
for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
|
||||
var pagePromise = pdfDocument.getPage(pageNum);
|
||||
pagePromise.then(function(pdfPage) {
|
||||
var pageNum = pdfPage.pageNumber;
|
||||
var pageView = pages[pageNum - 1];
|
||||
if (!pageView.pdfPage) {
|
||||
// The pdfPage might already be set if we've already entered
|
||||
// pageView.draw()
|
||||
pageView.setPdfPage(pdfPage);
|
||||
}
|
||||
var thumbnailView = thumbnails[pageNum - 1];
|
||||
if (!thumbnailView.pdfPage) {
|
||||
thumbnailView.setPdfPage(pdfPage);
|
||||
}
|
||||
|
||||
var pageRef = pdfPage.ref;
|
||||
var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
|
||||
pagesRefMap[refStr] = pdfPage.pageNumber;
|
||||
|
||||
if (self.pendingRefStr && self.pendingRefStr === refStr) {
|
||||
self.pendingRefStrLoaded.resolve();
|
||||
}
|
||||
});
|
||||
pagePromises.push(pagePromise);
|
||||
}
|
||||
|
||||
PDFFindController.firstPagePromise.resolve();
|
||||
|
||||
PDFJS.Promise.all(pagePromises).then(function(pages) {
|
||||
pagesPromise.resolve(pages);
|
||||
});
|
||||
});
|
||||
|
||||
var prefsPromise = prefs.initializedPromise;
|
||||
var storePromise = store.initializedPromise;
|
||||
PDFJS.Promise.all([firstPagePromise, storePromise]).then(function() {
|
||||
PDFJS.Promise.all([firstPagePromise, prefsPromise, storePromise]).
|
||||
then(function() {
|
||||
var showPreviousViewOnLoad = prefs.get('showPreviousViewOnLoad');
|
||||
var defaultZoomValue = prefs.get('defaultZoomValue');
|
||||
|
||||
var storedHash = null;
|
||||
if (store.get('exists', false)) {
|
||||
if (showPreviousViewOnLoad && store.get('exists', false)) {
|
||||
var pageNum = store.get('page', '1');
|
||||
var zoom = store.get('zoom', PDFView.currentScale);
|
||||
var zoom = defaultZoomValue || store.get('zoom', PDFView.currentScale);
|
||||
var left = store.get('scrollLeft', '0');
|
||||
var top = store.get('scrollTop', '0');
|
||||
|
||||
storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
|
||||
left + ',' + top;
|
||||
} else if (defaultZoomValue) {
|
||||
storedHash = 'page=1&zoom=' + defaultZoomValue;
|
||||
}
|
||||
// Initialize the browsing history.
|
||||
PDFHistory.initialize(self.documentFingerprint);
|
||||
|
@ -2459,6 +2579,13 @@ var PDFView = {
|
|||
pdfDocument.getOutline().then(function(outline) {
|
||||
self.outline = new DocumentOutlineView(outline);
|
||||
document.getElementById('viewOutline').disabled = !outline;
|
||||
|
||||
if (outline && prefs.get('ifAvailableShowOutlineOnLoad')) {
|
||||
if (!self.sidebarOpen) {
|
||||
document.getElementById('sidebarToggle').click();
|
||||
}
|
||||
self.switchSidebarView('outline');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -2550,6 +2677,11 @@ var PDFView = {
|
|||
},
|
||||
|
||||
renderHighestPriority: function pdfViewRenderHighestPriority() {
|
||||
if (PDFView.idleTimeout) {
|
||||
clearTimeout(PDFView.idleTimeout);
|
||||
PDFView.idleTimeout = null;
|
||||
}
|
||||
|
||||
// Pages have a higher priority than thumbnails, so check them first.
|
||||
var visiblePages = this.getVisiblePages();
|
||||
var pageView = this.getHighestPriority(visiblePages, this.pages,
|
||||
|
@ -2564,9 +2696,25 @@ var PDFView = {
|
|||
var thumbView = this.getHighestPriority(visibleThumbs,
|
||||
this.thumbnails,
|
||||
this.thumbnailViewScroll.down);
|
||||
if (thumbView)
|
||||
if (thumbView) {
|
||||
this.renderView(thumbView, 'thumbnail');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PDFView.idleTimeout = setTimeout(function () {
|
||||
PDFView.cleanup();
|
||||
}, CLEANUP_TIMEOUT);
|
||||
},
|
||||
|
||||
cleanup: function pdfViewCleanup() {
|
||||
for (var i = 0, ii = this.pages.length; i < ii; i++) {
|
||||
if (this.pages[i] &&
|
||||
this.pages[i].renderingState !== RenderingStates.FINISHED) {
|
||||
this.pages[i].reset();
|
||||
}
|
||||
}
|
||||
this.pdfDocument.cleanup();
|
||||
},
|
||||
|
||||
getHighestPriority: function pdfViewGetHighestPriority(visible, views,
|
||||
|
@ -2643,8 +2791,10 @@ var PDFView = {
|
|||
PDFView.navigateTo(params.nameddest);
|
||||
return;
|
||||
}
|
||||
var pageNumber, dest;
|
||||
if ('page' in params) {
|
||||
var pageNumber = (params.page | 0) || 1;
|
||||
pageNumber = (params.page | 0) || 1;
|
||||
}
|
||||
if ('zoom' in params) {
|
||||
var zoomArgs = params.zoom.split(','); // scale,left,top
|
||||
// building destination array
|
||||
|
@ -2653,18 +2803,19 @@ var PDFView = {
|
|||
// it should stay as it is.
|
||||
var zoomArg = zoomArgs[0];
|
||||
var zoomArgNumber = parseFloat(zoomArg);
|
||||
if (zoomArgNumber)
|
||||
if (zoomArgNumber) {
|
||||
zoomArg = zoomArgNumber / 100;
|
||||
|
||||
var dest = [null, {name: 'XYZ'},
|
||||
}
|
||||
dest = [null, {name: 'XYZ'},
|
||||
zoomArgs.length > 1 ? (zoomArgs[1] | 0) : null,
|
||||
zoomArgs.length > 2 ? (zoomArgs[2] | 0) : null,
|
||||
zoomArg];
|
||||
var currentPage = this.pages[pageNumber - 1];
|
||||
currentPage.scrollIntoView(dest);
|
||||
} else {
|
||||
this.page = pageNumber; // simple page
|
||||
}
|
||||
if (dest) {
|
||||
var currentPage = this.pages[(pageNumber || this.page) - 1];
|
||||
currentPage.scrollIntoView(dest);
|
||||
} else if (pageNumber) {
|
||||
this.page = pageNumber; // simple page
|
||||
}
|
||||
if ('pagemode' in params) {
|
||||
var toggle = document.getElementById('sidebarToggle');
|
||||
|
@ -2935,12 +3086,11 @@ var PageView = function pageView(container, id, scale,
|
|||
this.rotation = 0;
|
||||
this.scale = scale || 1.0;
|
||||
this.viewport = defaultViewport;
|
||||
this.pdfPageRotate = defaultViewport.rotate;
|
||||
this.pdfPageRotate = defaultViewport.rotation;
|
||||
|
||||
this.renderingState = RenderingStates.INITIAL;
|
||||
this.resume = null;
|
||||
|
||||
this.textContent = null;
|
||||
this.textLayer = null;
|
||||
|
||||
this.zoomLayer = null;
|
||||
|
@ -2962,7 +3112,8 @@ var PageView = function pageView(container, id, scale,
|
|||
this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
|
||||
this.pdfPage = pdfPage;
|
||||
this.pdfPageRotate = pdfPage.rotate;
|
||||
this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS);
|
||||
var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
|
||||
this.viewport = pdfPage.getViewport(this.scale * CSS_UNITS, totalRotation);
|
||||
this.stats = pdfPage.stats;
|
||||
this.reset();
|
||||
};
|
||||
|
@ -3279,7 +3430,7 @@ var PageView = function pageView(container, id, scale,
|
|||
width / CSS_UNITS;
|
||||
heightScale = (PDFView.container.clientHeight - SCROLLBAR_PADDING) /
|
||||
height / CSS_UNITS;
|
||||
scale = Math.min(widthScale, heightScale);
|
||||
scale = Math.min(Math.abs(widthScale), Math.abs(heightScale));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -3313,10 +3464,9 @@ var PageView = function pageView(container, id, scale,
|
|||
};
|
||||
|
||||
this.getTextContent = function pageviewGetTextContent() {
|
||||
if (!this.textContent) {
|
||||
this.textContent = this.pdfPage.getTextContent();
|
||||
}
|
||||
return this.textContent;
|
||||
return PDFView.getPage(this.id).then(function(pdfPage) {
|
||||
return pdfPage.getTextContent();
|
||||
});
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw(callback) {
|
||||
|
@ -3364,8 +3514,8 @@ var PageView = function pageView(container, id, scale,
|
|||
outputScale.scaled = true;
|
||||
}
|
||||
|
||||
canvas.width = Math.floor(viewport.width * outputScale.sx);
|
||||
canvas.height = Math.floor(viewport.height * outputScale.sy);
|
||||
canvas.width = (Math.floor(viewport.width) * outputScale.sx) | 0;
|
||||
canvas.height = (Math.floor(viewport.height) * outputScale.sy) | 0;
|
||||
canvas.style.width = Math.floor(viewport.width) + 'px';
|
||||
canvas.style.height = Math.floor(viewport.height) + 'px';
|
||||
// Add the viewport so it's known what it was originally drawn with.
|
||||
|
@ -3398,6 +3548,8 @@ var PageView = function pageView(container, id, scale,
|
|||
(1 / outputScale.sy) + ')';
|
||||
CustomStyle.setProp('transform' , textLayerDiv, cssScale);
|
||||
CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
|
||||
textLayerDiv.dataset._scaleX = outputScale.sx;
|
||||
textLayerDiv.dataset._scaleY = outputScale.sy;
|
||||
}
|
||||
|
||||
// Checking if document fonts are used only once
|
||||
|
@ -3813,27 +3965,24 @@ var TextLayerBuilder = function textLayerBuilder(options) {
|
|||
if ('isWhitespace' in textDiv.dataset) {
|
||||
continue;
|
||||
}
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
|
||||
ctx.font = textDiv.style.fontSize + ' ' + textDiv.style.fontFamily;
|
||||
var width = ctx.measureText(textDiv.textContent).width;
|
||||
|
||||
if (width > 0) {
|
||||
textLayerFrag.appendChild(textDiv);
|
||||
var textScale = textDiv.dataset.canvasWidth / width;
|
||||
var rotation = textDiv.dataset.angle;
|
||||
var transform = 'scale(' + textScale + ', 1)';
|
||||
transform = 'rotate(' + rotation + 'deg) ' + transform;
|
||||
CustomStyle.setProp('transform' , textDiv, transform);
|
||||
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
|
||||
|
||||
textLayerDiv.appendChild(textDiv);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderingDone = true;
|
||||
this.updateMatches();
|
||||
|
||||
textLayerDiv.appendChild(textLayerFrag);
|
||||
this.renderingDone = true;
|
||||
this.updateMatches();
|
||||
};
|
||||
|
||||
this.setupRenderLayoutTimer = function textLayerSetupRenderLayoutTimer() {
|
||||
|
@ -4587,7 +4736,7 @@ window.addEventListener('keydown', function keydown(evt) {
|
|||
switch (evt.keyCode) {
|
||||
case 70: // f
|
||||
if (!PDFView.supportsIntegratedFind) {
|
||||
PDFFindBar.toggle();
|
||||
PDFFindBar.open();
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
@ -4629,6 +4778,11 @@ window.addEventListener('keydown', function keydown(evt) {
|
|||
SecondaryToolbar.presentationModeClick();
|
||||
handled = true;
|
||||
break;
|
||||
case 71: // g
|
||||
// focuses input#pageNumber field
|
||||
document.getElementById('pageNumber').select();
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ content/PdfJs.jsm
|
|||
content/PdfJsTelemetry.jsm
|
||||
content/build/pdf.js
|
||||
content/build/pdf.worker.js
|
||||
content/default_preferences.js
|
||||
content/network.js
|
||||
content/web/debugger.js
|
||||
content/web/images/annotation-check.svg
|
||||
|
|
|
@ -1187,10 +1187,14 @@ paintflashingManual=Draw repainted areas in different colors
|
|||
# tooltip of button in devtools toolbox which toggles paint flashing.
|
||||
paintflashingTooltip=Highlight painted area
|
||||
|
||||
# LOCALIZATION NOTE (paintflashingOnDesc) A very short string used to describe the
|
||||
# function of the "paintflashing on" command.
|
||||
# LOCALIZATION NOTE (paintflashingToggleDesc) A very short string used to describe the
|
||||
# function of the "paintflashing toggle" command.
|
||||
paintflashingToggleDesc=Toggle paint flashing
|
||||
|
||||
# LOCALIZATION NOTE (splitconsoleTooltip) A string displayed as the
|
||||
# tooltip of button in devtools toolbox which toggles the split webconsole.
|
||||
splitconsoleTooltip=Toggle split console
|
||||
|
||||
# LOCALIZATION NOTE (appCacheDesc) A very short string used to describe the
|
||||
# function of the "appcache" command
|
||||
appCacheDesc=Application cache utilities
|
||||
|
|
|
@ -115,10 +115,11 @@ var Appbar = {
|
|||
onMenuButton: function(aEvent) {
|
||||
let typesArray = [];
|
||||
|
||||
if (!BrowserUI.isStartTabVisible)
|
||||
if (!BrowserUI.isStartTabVisible) {
|
||||
typesArray.push("find-in-page");
|
||||
if (ContextCommands.getPageSource())
|
||||
typesArray.push("view-page-source");
|
||||
}
|
||||
if (ContextCommands.getStoreLink())
|
||||
typesArray.push("ms-meta-data");
|
||||
if (ConsolePanelView.enabled)
|
||||
|
|
|
@ -265,10 +265,10 @@ Desktop browser's sync prefs.
|
|||
<hbox id="toolbar-context-page" pack="end">
|
||||
<circularprogressindicator id="download-progress" class="appbar-primary"
|
||||
oncommand="MetroDownloadsView.onDownloadButton()"/>
|
||||
<toolbarbutton id="star-button" class="appbar-primary"
|
||||
<toolbarbutton id="star-button" class="appbar-primary hide-on-start"
|
||||
type="checkbox"
|
||||
oncommand="Appbar.onStarButton()"/>
|
||||
<toolbarbutton id="pin-button" class="appbar-primary"
|
||||
<toolbarbutton id="pin-button" class="appbar-primary hide-on-start"
|
||||
type="checkbox"
|
||||
oncommand="Appbar.onPinButton()"/>
|
||||
<toolbarbutton id="menu-button" class="appbar-primary"
|
||||
|
|
|
@ -118,6 +118,14 @@ var ContextMenuHandler = {
|
|||
} else {
|
||||
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
|
||||
}
|
||||
} else if (this._target.isContentEditable) {
|
||||
try {
|
||||
this._target.ownerDocument.execCommand("paste",
|
||||
false,
|
||||
Ci.nsIClipboard.kGlobalClipboard);
|
||||
} catch (ex) {
|
||||
dump("ContextMenuHandler: exception pasting into contentEditable: " + ex.message + "\n");
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
@ -134,6 +142,12 @@ var ContextMenuHandler = {
|
|||
} else {
|
||||
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
|
||||
}
|
||||
} else if (this._target.isContentEditable) {
|
||||
try {
|
||||
this._target.ownerDocument.execCommand("cut", false);
|
||||
} catch (ex) {
|
||||
dump("ContextMenuHandler: exception cutting from contentEditable: " + ex.message + "\n");
|
||||
}
|
||||
}
|
||||
this.reset();
|
||||
},
|
||||
|
@ -188,12 +202,13 @@ var ContextMenuHandler = {
|
|||
contentDisposition: "",
|
||||
string: "",
|
||||
};
|
||||
let uniqueStateTypes = new Set();
|
||||
|
||||
// Do checks for nodes that never have children.
|
||||
if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
// See if the user clicked on an image.
|
||||
if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) {
|
||||
state.types.push("image");
|
||||
uniqueStateTypes.add("image");
|
||||
state.label = state.mediaURL = popupNode.currentURI.spec;
|
||||
imageUrl = state.mediaURL;
|
||||
this._target = popupNode;
|
||||
|
@ -218,6 +233,7 @@ var ContextMenuHandler = {
|
|||
|
||||
let elem = popupNode;
|
||||
let isText = false;
|
||||
let isEditableText = false;
|
||||
|
||||
while (elem) {
|
||||
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
|
@ -230,7 +246,7 @@ var ContextMenuHandler = {
|
|||
continue;
|
||||
}
|
||||
|
||||
state.types.push("link");
|
||||
uniqueStateTypes.add("link");
|
||||
state.label = state.linkURL = this._getLinkURL(elem);
|
||||
linkUrl = state.linkURL;
|
||||
state.linkTitle = popupNode.textContent || popupNode.title;
|
||||
|
@ -238,49 +254,61 @@ var ContextMenuHandler = {
|
|||
// mark as text so we can pickup on selection below
|
||||
isText = true;
|
||||
break;
|
||||
} else if (Util.isTextInput(elem)) {
|
||||
}
|
||||
// is the target contentEditable (not just inheriting contentEditable)
|
||||
else if (elem.contentEditable == "true") {
|
||||
this._target = elem;
|
||||
isEditableText = true;
|
||||
isText = true;
|
||||
uniqueStateTypes.add("input-text");
|
||||
|
||||
if (elem.textContent.length) {
|
||||
uniqueStateTypes.add("selectable");
|
||||
} else {
|
||||
uniqueStateTypes.add("input-empty");
|
||||
}
|
||||
break;
|
||||
}
|
||||
// is the target a text input
|
||||
else if (Util.isTextInput(elem)) {
|
||||
this._target = elem;
|
||||
isEditableText = true;
|
||||
uniqueStateTypes.add("input-text");
|
||||
|
||||
let selectionStart = elem.selectionStart;
|
||||
let selectionEnd = elem.selectionEnd;
|
||||
|
||||
state.types.push("input-text");
|
||||
this._target = elem;
|
||||
|
||||
// Don't include "copy" for password fields.
|
||||
if (!(elem instanceof Ci.nsIDOMHTMLInputElement) || elem.mozIsTextField(true)) {
|
||||
// If there is a selection add cut and copy
|
||||
if (selectionStart != selectionEnd) {
|
||||
state.types.push("cut");
|
||||
state.types.push("copy");
|
||||
uniqueStateTypes.add("cut");
|
||||
uniqueStateTypes.add("copy");
|
||||
state.string = elem.value.slice(selectionStart, selectionEnd);
|
||||
} else if (elem.value && elem.textLength) {
|
||||
// There is text and it is not selected so add selectable items
|
||||
state.types.push("selectable");
|
||||
uniqueStateTypes.add("selectable");
|
||||
state.string = elem.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!elem.textLength) {
|
||||
state.types.push("input-empty");
|
||||
}
|
||||
|
||||
let flavors = ["text/unicode"];
|
||||
let cb = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||
let hasData = cb.hasDataMatchingFlavors(flavors,
|
||||
flavors.length,
|
||||
Ci.nsIClipboard.kGlobalClipboard);
|
||||
if (hasData && !elem.readOnly) {
|
||||
state.types.push("paste");
|
||||
uniqueStateTypes.add("input-empty");
|
||||
}
|
||||
break;
|
||||
} else if (Util.isText(elem)) {
|
||||
}
|
||||
// is the target an element containing text content
|
||||
else if (Util.isText(elem)) {
|
||||
isText = true;
|
||||
} else if (elem instanceof Ci.nsIDOMHTMLMediaElement ||
|
||||
}
|
||||
// is the target a media element
|
||||
else if (elem instanceof Ci.nsIDOMHTMLMediaElement ||
|
||||
elem instanceof Ci.nsIDOMHTMLVideoElement) {
|
||||
state.label = state.mediaURL = (elem.currentSrc || elem.src);
|
||||
state.types.push((elem.paused || elem.ended) ?
|
||||
uniqueStateTypes.add((elem.paused || elem.ended) ?
|
||||
"media-paused" : "media-playing");
|
||||
if (elem instanceof Ci.nsIDOMHTMLVideoElement) {
|
||||
state.types.push("video");
|
||||
uniqueStateTypes.add("video");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -295,20 +323,37 @@ var ContextMenuHandler = {
|
|||
let selection = targetWindow.getSelection();
|
||||
if (selection && this._tapInSelection(selection, aX, aY)) {
|
||||
state.string = targetWindow.getSelection().toString();
|
||||
state.types.push("copy");
|
||||
state.types.push("selected-text");
|
||||
uniqueStateTypes.add("copy");
|
||||
uniqueStateTypes.add("selected-text");
|
||||
if (isEditableText) {
|
||||
uniqueStateTypes.add("cut");
|
||||
}
|
||||
} else {
|
||||
// Add general content text if this isn't anything specific
|
||||
if (state.types.indexOf("image") == -1 &&
|
||||
state.types.indexOf("media") == -1 &&
|
||||
state.types.indexOf("video") == -1 &&
|
||||
state.types.indexOf("link") == -1 &&
|
||||
state.types.indexOf("input-text") == -1) {
|
||||
state.types.push("content-text");
|
||||
if (!(
|
||||
uniqueStateTypes.has("image") ||
|
||||
uniqueStateTypes.has("media") ||
|
||||
uniqueStateTypes.has("video") ||
|
||||
uniqueStateTypes.has("link") ||
|
||||
uniqueStateTypes.has("input-text")
|
||||
)) {
|
||||
uniqueStateTypes.add("content-text");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is paste applicable here?
|
||||
if (isEditableText) {
|
||||
let flavors = ["text/unicode"];
|
||||
let cb = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
|
||||
let hasData = cb.hasDataMatchingFlavors(flavors,
|
||||
flavors.length,
|
||||
Ci.nsIClipboard.kGlobalClipboard);
|
||||
// add paste if there's data
|
||||
if (hasData && !elem.readOnly) {
|
||||
uniqueStateTypes.add("paste");
|
||||
}
|
||||
}
|
||||
// populate position and event source
|
||||
state.xPos = offsetX + aX;
|
||||
state.yPos = offsetY + aY;
|
||||
|
@ -316,8 +361,9 @@ var ContextMenuHandler = {
|
|||
|
||||
for (let i = 0; i < this._types.length; i++)
|
||||
if (this._types[i].handler(state, popupNode))
|
||||
state.types.push(this._types[i].name);
|
||||
uniqueStateTypes.add(this._types[i].name);
|
||||
|
||||
state.types = [type for (type of uniqueStateTypes)];
|
||||
this._previousState = state;
|
||||
|
||||
sendAsyncMessage("Content:ContextMenu", state);
|
||||
|
|
|
@ -109,3 +109,24 @@
|
|||
<!ENTITY optionsFlyout.key "o">
|
||||
<!ENTITY syncFlyout.key "s">
|
||||
<!ENTITY aboutFlyout.key "a">
|
||||
|
||||
<!-- FIRT RUN EXPERIENCE -->
|
||||
<!-- LOCALIZATION NOTE (firstRunTabs.label,
|
||||
firstRunTopSites.label,
|
||||
firstRunBookmarks.label,
|
||||
firstRunMenu.label,
|
||||
firstRunHistory.label )
|
||||
These strings appear on the Firefox Start page the first time
|
||||
Firefox for Windows 8 Touch (Metro) is launched. Each one
|
||||
has an arrow pointing toward the feature it references. The code
|
||||
to display these strings is not enabled yet, but will be soon.
|
||||
For now, you can see this mockup for an example of how they are
|
||||
used: https://bug941284.bugzilla.mozilla.org/attachment.cgi?id=8344046
|
||||
-->
|
||||
<!ENTITY firstRunTabs.label "Looking for your tabs? Just pull down or right-click">
|
||||
<!ENTITY firstRunTopSites.label "Go to the sites you visit most">
|
||||
<!ENTITY firstRunBookmarks.label "Find pages you've saved for later">
|
||||
<!ENTITY firstRunMenu.label "Access more features and options">
|
||||
<!ENTITY firstRunHistory.label "See where you've been on the Web">
|
||||
<!ENTITY firstRunWelcome.label "Welcome to &brandShortName;">
|
||||
<!ENTITY firstRunDifferent.label "Different by design">
|
||||
|
|
|
@ -714,6 +714,7 @@ documenttab[selected] .documenttab-selection {
|
|||
/* Contextual toolbar controls */
|
||||
|
||||
#toolbar-context-autocomplete,
|
||||
.hide-on-start,
|
||||
#toolbar-context-page {
|
||||
transition-property: opacity, visibility;
|
||||
transition-duration: @forward_transition_length@;
|
||||
|
@ -721,7 +722,7 @@ documenttab[selected] .documenttab-selection {
|
|||
}
|
||||
|
||||
#toolbar-contextual:not([autocomplete]) #toolbar-context-autocomplete,
|
||||
#toolbar-contextual[startpage] #toolbar-context-page,
|
||||
#toolbar-contextual[startpage] .hide-on-start,
|
||||
#toolbar-contextual[autocomplete] #toolbar-context-page {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 695 B |
|
@ -156,6 +156,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
|
||||
skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
|
||||
skin/classic/browser/devtools/command-tilt.png (devtools/command-tilt.png)
|
||||
skin/classic/browser/devtools/command-console.png (devtools/command-console.png)
|
||||
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 695 B |
|
@ -258,6 +258,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
|
||||
skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
|
||||
skin/classic/browser/devtools/command-tilt.png (devtools/command-tilt.png)
|
||||
skin/classic/browser/devtools/command-console.png (devtools/command-console.png)
|
||||
skin/classic/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
|
||||
|
|
|
@ -441,6 +441,23 @@
|
|||
-moz-image-region: rect(0px, 48px, 16px, 32px);
|
||||
}
|
||||
|
||||
#command-button-splitconsole {
|
||||
list-style-image: url("chrome://browser/skin/devtools/command-console.png");
|
||||
-moz-image-region: rect(0px, 16px, 16px, 0px);
|
||||
}
|
||||
|
||||
#command-button-splitconsole:hover {
|
||||
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
#command-button-splitconsole:hover:active {
|
||||
-moz-image-region: rect(0px, 48px, 16px, 32px);
|
||||
}
|
||||
|
||||
#command-button-splitconsole[checked=true] {
|
||||
-moz-image-region: rect(0px, 64px, 16px, 48px);
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
|
||||
.devtools-tabbar {
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 695 B |
|
@ -186,6 +186,7 @@ browser.jar:
|
|||
skin/classic/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
|
||||
skin/classic/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
|
||||
skin/classic/browser/devtools/command-tilt.png (devtools/command-tilt.png)
|
||||
skin/classic/browser/devtools/command-console.png (devtools/command-console.png)
|
||||
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)
|
||||
skin/classic/browser/devtools/editor-error.png (devtools/editor-error.png)
|
||||
skin/classic/browser/devtools/editor-breakpoint.png (devtools/editor-breakpoint.png)
|
||||
|
@ -487,6 +488,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/devtools/command-responsivemode.png (devtools/command-responsivemode.png)
|
||||
skin/classic/aero/browser/devtools/command-scratchpad.png (devtools/command-scratchpad.png)
|
||||
skin/classic/aero/browser/devtools/command-tilt.png (devtools/command-tilt.png)
|
||||
skin/classic/aero/browser/devtools/command-console.png (devtools/command-console.png)
|
||||
skin/classic/aero/browser/devtools/alerticon-warning.png (devtools/alerticon-warning.png)
|
||||
skin/classic/aero/browser/devtools/ruleview.css (devtools/ruleview.css)
|
||||
skin/classic/aero/browser/devtools/commandline.css (devtools/commandline.css)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
public class TestConstants {
|
||||
public static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
|
||||
|
|
|
@ -12,7 +12,6 @@ import org.mozilla.gecko.FennecMochitestAssert;
|
|||
import org.mozilla.gecko.FennecNativeActions;
|
||||
import org.mozilla.gecko.FennecNativeDriver;
|
||||
import org.mozilla.gecko.FennecTalosAssert;
|
||||
import org.mozilla.gecko.TestConstants;
|
||||
import org.mozilla.gecko.tests.components.*;
|
||||
import org.mozilla.gecko.tests.helpers.*;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public class AboutHomeComponent extends BaseComponent {
|
|||
(expectedPageIndex >= 0) ? expectedPageIndex : Page.values().length - 1;
|
||||
}
|
||||
|
||||
assertEquals("The current HomePager page is " + Page.values()[expectedPageIndex],
|
||||
assertEquals("The current HomePager page is " + expectedPage,
|
||||
expectedPageIndex, getHomePagerView().getCurrentItem());
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -6631,7 +6631,27 @@ var SearchEngines = {
|
|||
|
||||
let filter = {
|
||||
matches: function (aElement) {
|
||||
return (aElement.form && NativeWindow.contextmenus.textContext.matches(aElement));
|
||||
// Copied from body of isTargetAKeywordField function in nsContextMenu.js
|
||||
if(!(aElement instanceof HTMLInputElement))
|
||||
return false;
|
||||
let form = aElement.form;
|
||||
if (!form || aElement.type == "password")
|
||||
return false;
|
||||
|
||||
let method = form.method.toUpperCase();
|
||||
|
||||
// These are the following types of forms we can create keywords for:
|
||||
//
|
||||
// method encoding type can create keyword
|
||||
// GET * YES
|
||||
// * YES
|
||||
// POST * YES
|
||||
// POST application/x-www-form-urlencoded YES
|
||||
// POST text/plain NO ( a little tricky to do)
|
||||
// POST multipart/form-data NO
|
||||
// POST everything else YES
|
||||
return (method == "GET" || method == "") ||
|
||||
(form.enctype != "text/plain") && (form.enctype != "multipart/form-data");
|
||||
}
|
||||
};
|
||||
SelectionHandler.actions.SEARCH_ADD = {
|
||||
|
|
Загрузка…
Ссылка в новой задаче