Merge branch 'refs/heads/master' into fix-worker-loader
Conflicts: src/core.js
This commit is contained in:
commit
7611a766c7
1
LICENSE
1
LICENSE
@ -9,6 +9,7 @@
|
||||
Yury Delendik
|
||||
Kalervo Kujala
|
||||
Adil Allawi <@ironymark>
|
||||
Jakob Miland <saebekassebil@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
13
Makefile
13
Makefile
@ -43,6 +43,10 @@ PDF_JS_FILES = \
|
||||
server:
|
||||
@cd test; python test.py --port=8888;
|
||||
|
||||
# make test
|
||||
#
|
||||
# This target runs all the tests excluding the unit-test. This can be used for
|
||||
# testing all browsers.
|
||||
test: shell-test browser-test
|
||||
|
||||
#
|
||||
@ -69,6 +73,13 @@ bundle: | $(BUILD_DIR)
|
||||
rm -f *.tmp; \
|
||||
cd ..
|
||||
|
||||
# make unit-test
|
||||
#
|
||||
# This target runs in-browser unit tests with js-test-driver and jasmine unit
|
||||
# test framework.
|
||||
unit-test:
|
||||
@cd test/unit/ ; make ;
|
||||
|
||||
# make browser-test
|
||||
#
|
||||
# This target runs in-browser tests using two primary arguments: a
|
||||
@ -129,7 +140,7 @@ browser-test:
|
||||
#
|
||||
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
||||
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
||||
extensions/firefox/components extensions/chrome
|
||||
extensions/firefox/components extensions/chrome test/unit
|
||||
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
||||
lint:
|
||||
gjslint --nojsdoc $(GJSLINT_FILES)
|
||||
|
676
external/jasmine/jasmine-html.js
vendored
676
external/jasmine/jasmine-html.js
vendored
@ -1,676 +0,0 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = doc.location.search.substring(1).split('&');
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'}),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
81
external/jasmine/jasmine.css
vendored
81
external/jasmine/jasmine.css
vendored
@ -1,81 +0,0 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
BIN
external/jasmine/jasmine_favicon.png
vendored
BIN
external/jasmine/jasmine_favicon.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 905 B |
198
external/jasmineAdapter/JasmineAdapter.js
vendored
Normal file
198
external/jasmineAdapter/JasmineAdapter.js
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* @fileoverview Jasmine JsTestDriver Adapter.
|
||||
* @author misko@hevery.com (Misko Hevery)
|
||||
*/
|
||||
(function(window) {
|
||||
var rootDescribes = new Describes(window);
|
||||
var describePath = [];
|
||||
rootDescribes.collectMode();
|
||||
|
||||
var JASMINE_TYPE = 'jasmine test case';
|
||||
TestCase('Jasmine Adapter Tests', null, JASMINE_TYPE);
|
||||
|
||||
var jasminePlugin = {
|
||||
name:'jasmine',
|
||||
|
||||
getTestRunsConfigurationFor: function(testCaseInfos, expressions, testRunsConfiguration) {
|
||||
for (var i = 0; i < testCaseInfos.length; i++) {
|
||||
if (testCaseInfos[i].getType() == JASMINE_TYPE) {
|
||||
testRunsConfiguration.push(new jstestdriver.TestRunConfiguration(testCaseInfos[i], []));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
runTestConfiguration: function(testRunConfiguration, onTestDone, onTestRunConfigurationComplete){
|
||||
if (testRunConfiguration.getTestCaseInfo().getType() != JASMINE_TYPE) return false;
|
||||
|
||||
var jasmineEnv = jasmine.currentEnv_ = new jasmine.Env();
|
||||
rootDescribes.playback();
|
||||
var specLog = jstestdriver.console.log_ = [];
|
||||
var start;
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return rootDescribes.isExclusive(spec);
|
||||
};
|
||||
jasmineEnv.reporter = {
|
||||
log: function(str){
|
||||
specLog.push(str);
|
||||
},
|
||||
|
||||
reportRunnerStarting: function(runner) { },
|
||||
|
||||
reportSpecStarting: function(spec) {
|
||||
specLog = jstestdriver.console.log_ = [];
|
||||
start = new Date().getTime();
|
||||
},
|
||||
|
||||
reportSpecResults: function(spec) {
|
||||
var suite = spec.suite;
|
||||
var results = spec.results();
|
||||
if (results.skipped) return;
|
||||
var end = new Date().getTime();
|
||||
var messages = [];
|
||||
var resultItems = results.getItems();
|
||||
var state = 'passed';
|
||||
for ( var i = 0; i < resultItems.length; i++) {
|
||||
if (!resultItems[i].passed()) {
|
||||
state = resultItems[i].message.match(/AssertionError:/) ? 'error' : 'failed';
|
||||
messages.push({
|
||||
message: resultItems[i].toString(),
|
||||
name: resultItems[i].trace.name,
|
||||
stack: formatStack(resultItems[i].trace.stack)
|
||||
});
|
||||
}
|
||||
}
|
||||
onTestDone(
|
||||
new jstestdriver.TestResult(
|
||||
suite.getFullName(),
|
||||
spec.description,
|
||||
state,
|
||||
jstestdriver.angular.toJson(messages),
|
||||
specLog.join('\n'),
|
||||
end - start));
|
||||
},
|
||||
|
||||
reportSuiteResults: function(suite) {},
|
||||
|
||||
reportRunnerResults: function(runner) {
|
||||
onTestRunConfigurationComplete();
|
||||
}
|
||||
};
|
||||
jasmineEnv.execute();
|
||||
return true;
|
||||
},
|
||||
|
||||
onTestsFinish: function(){
|
||||
jasmine.currentEnv_ = null;
|
||||
rootDescribes.collectMode();
|
||||
}
|
||||
};
|
||||
jstestdriver.pluginRegistrar.register(jasminePlugin);
|
||||
|
||||
function formatStack(stack) {
|
||||
var lines = (stack||'').split(/\r?\n/);
|
||||
var frames = [];
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].match(/\/jasmine[\.-]/)) {
|
||||
frames.push(lines[i].replace(/https?:\/\/\w+(:\d+)?\/test\//, '').replace(/^\s*/, ' '));
|
||||
}
|
||||
}
|
||||
return frames.join('\n');
|
||||
}
|
||||
|
||||
function noop(){}
|
||||
function Describes(window){
|
||||
var describes = {};
|
||||
var beforeEachs = {};
|
||||
var afterEachs = {};
|
||||
// Here we store:
|
||||
// 0: everyone runs
|
||||
// 1: run everything under ddescribe
|
||||
// 2: run only iits (ignore ddescribe)
|
||||
var exclusive = 0;
|
||||
var collectMode = true;
|
||||
intercept('describe', describes);
|
||||
intercept('xdescribe', describes);
|
||||
intercept('beforeEach', beforeEachs);
|
||||
intercept('afterEach', afterEachs);
|
||||
|
||||
function intercept(functionName, collection){
|
||||
window[functionName] = function(desc, fn){
|
||||
if (collectMode) {
|
||||
collection[desc] = function(){
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
};
|
||||
} else {
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
}
|
||||
};
|
||||
}
|
||||
window.ddescribe = function(name, fn){
|
||||
if (exclusive < 1) {
|
||||
exclusive = 1; // run ddescribe only
|
||||
}
|
||||
window.describe(name, function(){
|
||||
var oldIt = window.it;
|
||||
window.it = function(name, fn){
|
||||
fn.exclusive = 1; // run anything under ddescribe
|
||||
jasmine.getEnv().it(name, fn);
|
||||
};
|
||||
try {
|
||||
fn.call(this);
|
||||
} finally {
|
||||
window.it = oldIt;
|
||||
};
|
||||
});
|
||||
};
|
||||
window.iit = function(name, fn){
|
||||
exclusive = fn.exclusive = 2; // run only iits
|
||||
jasmine.getEnv().it(name, fn);
|
||||
};
|
||||
|
||||
|
||||
this.collectMode = function() {
|
||||
collectMode = true;
|
||||
exclusive = 0; // run everything
|
||||
};
|
||||
this.playback = function(){
|
||||
collectMode = false;
|
||||
playback(beforeEachs);
|
||||
playback(afterEachs);
|
||||
playback(describes);
|
||||
|
||||
function playback(set) {
|
||||
for ( var name in set) {
|
||||
set[name]();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.isExclusive = function(spec) {
|
||||
if (exclusive) {
|
||||
var blocks = spec.queue.blocks;
|
||||
for ( var i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i].func.exclusive >= exclusive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
})(window);
|
||||
|
||||
// Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
23
external/jasmineAdapter/MIT.LICENSE
vendored
Normal file
23
external/jasmineAdapter/MIT.LICENSE
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Copyright (c) 2010
|
||||
Misko Hevery <misko@hevery.com>
|
||||
Olmo Maldonado <me@ibolmo.com>
|
||||
Christoph Pojer <christoph.pojer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
BIN
external/jsTestDriver/JsTestDriver-1.3.3d.jar
vendored
Normal file
BIN
external/jsTestDriver/JsTestDriver-1.3.3d.jar
vendored
Normal file
Binary file not shown.
202
external/jsTestDriver/LICENSE-2.0.txt
vendored
Normal file
202
external/jsTestDriver/LICENSE-2.0.txt
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
@ -255,8 +255,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
// Scale so that canvas units are the same as PDF user space units
|
||||
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
||||
this.textDivs = [];
|
||||
this.textLayerQueue = [];
|
||||
// Move the media left-top corner to the (0,0) canvas position
|
||||
this.ctx.translate(-mediaBox.x, -mediaBox.y);
|
||||
|
||||
if (this.textLayer)
|
||||
this.textLayer.beginLayout();
|
||||
},
|
||||
|
||||
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||
@ -320,27 +323,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
endDrawing: function canvasGraphicsEndDrawing() {
|
||||
this.ctx.restore();
|
||||
|
||||
var textLayer = this.textLayer;
|
||||
if (!textLayer)
|
||||
return;
|
||||
|
||||
var self = this;
|
||||
var textDivs = this.textDivs;
|
||||
this.textLayerTimer = setInterval(function renderTextLayer() {
|
||||
if (textDivs.length === 0) {
|
||||
clearInterval(self.textLayerTimer);
|
||||
return;
|
||||
}
|
||||
var textDiv = textDivs.shift();
|
||||
if (textDiv.dataset.textLength > 1) { // avoid div by zero
|
||||
textLayer.appendChild(textDiv);
|
||||
// Adjust div width (via letterSpacing) to match canvas text
|
||||
// Due to the .offsetWidth calls, this is slow
|
||||
textDiv.style.letterSpacing =
|
||||
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
||||
(textDiv.dataset.textLength - 1)) + 'px';
|
||||
}
|
||||
}, 0);
|
||||
if (this.textLayer)
|
||||
this.textLayer.endLayout();
|
||||
},
|
||||
|
||||
// Graphics state
|
||||
@ -359,6 +343,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||
this.ctx.mozDash = dashArray;
|
||||
this.ctx.mozDashOffset = dashPhase;
|
||||
this.ctx.webkitLineDash = dashArray;
|
||||
this.ctx.webkitLineDashOffset = dashPhase;
|
||||
},
|
||||
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||
TODO('set rendering intent: ' + intent);
|
||||
@ -630,24 +616,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
return geometry;
|
||||
},
|
||||
|
||||
pushTextDivs: function canvasGraphicsPushTextDivs(text) {
|
||||
var div = document.createElement('div');
|
||||
var fontSize = this.current.fontSize;
|
||||
|
||||
// vScale and hScale already contain the scaling to pixel units
|
||||
// as mozCurrentTransform reflects ctx.scale() changes
|
||||
// (see beginDrawing())
|
||||
var fontHeight = fontSize * text.geom.vScale;
|
||||
div.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
|
||||
|
||||
div.style.fontSize = fontHeight + 'px';
|
||||
div.style.fontFamily = this.current.font.loadedName || 'sans-serif';
|
||||
div.style.left = text.geom.x + 'px';
|
||||
div.style.top = (text.geom.y - fontHeight) + 'px';
|
||||
div.innerHTML = text.str;
|
||||
div.dataset.textLength = text.length;
|
||||
this.textDivs.push(div);
|
||||
},
|
||||
showText: function canvasGraphicsShowText(str, skipTextSelection) {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
@ -672,6 +640,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
ctx.translate(current.x, current.y);
|
||||
|
||||
ctx.scale(textHScale, 1);
|
||||
ctx.lineWidth /= current.textMatrix[0];
|
||||
|
||||
if (textSelection) {
|
||||
this.save();
|
||||
@ -708,6 +677,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
} else {
|
||||
ctx.save();
|
||||
this.applyTextTransforms();
|
||||
ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0];
|
||||
|
||||
if (textSelection)
|
||||
text.geom = this.getTextGeometry();
|
||||
|
||||
@ -744,7 +715,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
width += charWidth;
|
||||
|
||||
text.str += glyph.unicode === ' ' ? ' ' : glyph.unicode;
|
||||
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||
text.length++;
|
||||
text.canvasWidth += charWidth;
|
||||
}
|
||||
@ -753,7 +724,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
|
||||
if (textSelection)
|
||||
this.pushTextDivs(text);
|
||||
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||
|
||||
return text;
|
||||
},
|
||||
@ -796,7 +767,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
|
||||
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
|
||||
if (numFakeSpaces > 0) {
|
||||
text.str += ' ';
|
||||
text.str += '\u00A0';
|
||||
text.length++;
|
||||
}
|
||||
}
|
||||
@ -806,7 +777,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
if (textSelection) {
|
||||
if (shownText.str === ' ') {
|
||||
text.str += ' ';
|
||||
text.str += '\u00A0';
|
||||
} else {
|
||||
text.str += shownText.str;
|
||||
}
|
||||
@ -819,7 +790,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
|
||||
if (textSelection)
|
||||
this.pushTextDivs(text);
|
||||
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||
},
|
||||
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||
this.nextLine();
|
||||
|
152
src/core.js
152
src/core.js
@ -70,8 +70,7 @@ var Page = (function PageClosure() {
|
||||
this.xref = xref;
|
||||
this.ref = ref;
|
||||
|
||||
this.ctx = null;
|
||||
this.callback = null;
|
||||
this.displayReadyPromise = null;
|
||||
}
|
||||
|
||||
Page.prototype = {
|
||||
@ -110,9 +109,11 @@ var Page = (function PageClosure() {
|
||||
width: this.width,
|
||||
height: this.height
|
||||
};
|
||||
var mediaBox = this.mediaBox;
|
||||
var offsetX = mediaBox[0], offsetY = mediaBox[1];
|
||||
if (isArray(obj) && obj.length == 4) {
|
||||
var tl = this.rotatePoint(obj[0], obj[1]);
|
||||
var br = this.rotatePoint(obj[2], obj[3]);
|
||||
var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY);
|
||||
var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY);
|
||||
view.x = Math.min(tl.x, br.x);
|
||||
view.y = Math.min(tl.y, br.y);
|
||||
view.width = Math.abs(tl.x - br.x);
|
||||
@ -165,20 +166,12 @@ var Page = (function PageClosure() {
|
||||
IRQueue, fonts) {
|
||||
var self = this;
|
||||
this.IRQueue = IRQueue;
|
||||
var gfx = new CanvasGraphics(this.ctx, this.objs, this.textLayer);
|
||||
|
||||
var displayContinuation = function pageDisplayContinuation() {
|
||||
// Always defer call to display() to work around bug in
|
||||
// Firefox error reporting from XHR callbacks.
|
||||
setTimeout(function pageSetTimeout() {
|
||||
try {
|
||||
self.display(gfx, self.callback);
|
||||
} catch (e) {
|
||||
if (self.callback)
|
||||
self.callback(e);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
self.displayReadyPromise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
@ -323,10 +316,10 @@ var Page = (function PageClosure() {
|
||||
if (a) {
|
||||
switch (a.get('S').name) {
|
||||
case 'URI':
|
||||
link.url = a.get('URI');
|
||||
item.url = a.get('URI');
|
||||
break;
|
||||
case 'GoTo':
|
||||
link.dest = a.get('D');
|
||||
item.dest = a.get('D');
|
||||
break;
|
||||
default:
|
||||
TODO('other link types');
|
||||
@ -334,7 +327,7 @@ var Page = (function PageClosure() {
|
||||
} else if (annotation.has('Dest')) {
|
||||
// simple destination link
|
||||
var dest = annotation.get('Dest');
|
||||
link.dest = isName(dest) ? dest.name : dest;
|
||||
item.dest = isName(dest) ? dest.name : dest;
|
||||
}
|
||||
break;
|
||||
case 'Widget':
|
||||
@ -379,18 +372,51 @@ var Page = (function PageClosure() {
|
||||
item.textAlignment = getInheritableProperty(annotation, 'Q');
|
||||
item.flags = getInheritableProperty(annotation, 'Ff') || 0;
|
||||
break;
|
||||
case 'Text':
|
||||
var content = annotation.get('Contents');
|
||||
var title = annotation.get('T');
|
||||
item.content = stringToPDFString(content || '');
|
||||
item.title = stringToPDFString(title || '');
|
||||
item.name = annotation.get('Name').name;
|
||||
break;
|
||||
default:
|
||||
TODO('unimplemented annotation type: ' + subtype.name);
|
||||
break;
|
||||
}
|
||||
items.push(item);
|
||||
}
|
||||
return items;
|
||||
},
|
||||
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||
this.ctx = ctx;
|
||||
this.callback = callback;
|
||||
this.textLayer = textLayer;
|
||||
|
||||
this.startRenderingTime = Date.now();
|
||||
this.pdf.startRendering(this);
|
||||
|
||||
// If there is no displayReadyPromise yet, then the IRQueue was never
|
||||
// requested before. Make the request and create the promise.
|
||||
if (!this.displayReadyPromise) {
|
||||
this.pdf.startRendering(this);
|
||||
this.displayReadyPromise = new Promise();
|
||||
}
|
||||
|
||||
// Once the IRQueue and fonts are loaded, perform the actual rendering.
|
||||
this.displayReadyPromise.then(
|
||||
function pageDisplayReadyPromise() {
|
||||
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
|
||||
try {
|
||||
this.display(gfx, callback);
|
||||
} catch (e) {
|
||||
if (callback)
|
||||
callback(e);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}.bind(this),
|
||||
function pageDisplayReadPromiseError(reason) {
|
||||
if (callback)
|
||||
callback(reason);
|
||||
else
|
||||
throw reason;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -513,10 +539,19 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
},
|
||||
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||
this.checkHeader();
|
||||
this.xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
this.catalog = new Catalog(this.xref);
|
||||
var xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
this.xref = xref;
|
||||
this.catalog = new Catalog(xref);
|
||||
if (xref.trailer && xref.trailer.has('ID')) {
|
||||
var fileID = '';
|
||||
var id = xref.fetchIfRef(xref.trailer.get('ID'))[0];
|
||||
id.split('').forEach(function(el) {
|
||||
fileID += Number(el.charCodeAt(0)).toString(16);
|
||||
});
|
||||
this.fileID = fileID;
|
||||
}
|
||||
},
|
||||
get numPages() {
|
||||
var linearization = this.linearization;
|
||||
@ -524,6 +559,22 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
// shadow the prototype getter
|
||||
return shadow(this, 'numPages', num);
|
||||
},
|
||||
getFingerprint: function pdfDocGetFingerprint() {
|
||||
if (this.fileID) {
|
||||
return this.fileID;
|
||||
} else {
|
||||
// If we got no fileID, then we generate one,
|
||||
// from the first 100 bytes of PDF
|
||||
var data = this.stream.bytes.subarray(0, 100);
|
||||
var hash = calculateMD5(data, 0, data.length);
|
||||
var strHash = '';
|
||||
for (var i = 0, length = hash.length; i < length; i++) {
|
||||
strHash += Number(hash[i]).toString(16);
|
||||
}
|
||||
|
||||
return strHash;
|
||||
}
|
||||
},
|
||||
getPage: function pdfDocGetPage(n) {
|
||||
return this.catalog.getPage(n);
|
||||
}
|
||||
@ -550,7 +601,7 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
this.data = data;
|
||||
this.stream = stream;
|
||||
this.pdf = new PDFDocModel(stream);
|
||||
|
||||
this.fingerprint = this.pdf.getFingerprint();
|
||||
this.catalog = this.pdf.catalog;
|
||||
this.objs = new PDFObjects();
|
||||
|
||||
@ -569,33 +620,34 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
throw 'No PDFJS.workerSrc specified';
|
||||
}
|
||||
|
||||
var worker;
|
||||
try {
|
||||
worker = new Worker(workerSrc);
|
||||
} catch (e) {
|
||||
// Some versions of FF can't create a worker on localhost, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||
globalScope.PDFJS.disableWorker = true;
|
||||
this.setupFakeWorker();
|
||||
var worker = new Worker(workerSrc);
|
||||
|
||||
var messageHandler = new MessageHandler('main', worker);
|
||||
|
||||
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||
if (supportTypedArray) {
|
||||
this.worker = worker;
|
||||
this.setupMessageHandler(messageHandler);
|
||||
} else {
|
||||
globalScope.PDFJS.disableWorker = true;
|
||||
this.setupFakeWorker();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
var testObj = new Uint8Array(1);
|
||||
// Some versions of Opera throw a DATA_CLONE_ERR on
|
||||
// serializing the typed array.
|
||||
messageHandler.send('test', testObj);
|
||||
return;
|
||||
}
|
||||
|
||||
var messageHandler = new MessageHandler('main', worker);
|
||||
|
||||
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||
if (supportTypedArray) {
|
||||
this.worker = worker;
|
||||
this.setupMessageHandler(messageHandler);
|
||||
} else {
|
||||
this.setupFakeWorker();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
var testObj = new Uint8Array(1);
|
||||
messageHandler.send('test', testObj);
|
||||
} else {
|
||||
this.setupFakeWorker();
|
||||
} catch (e) {}
|
||||
}
|
||||
// Either workers are disabled, not supported or have thrown an exception.
|
||||
// Thus, we fallback to a faked worker.
|
||||
globalScope.PDFJS.disableWorker = true;
|
||||
this.setupFakeWorker();
|
||||
}
|
||||
|
||||
PDFDoc.prototype = {
|
||||
@ -679,8 +731,8 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
|
||||
messageHandler.on('page_error', function pdfDocError(data) {
|
||||
var page = this.pageCache[data.pageNum];
|
||||
if (page.callback)
|
||||
page.callback(data.error);
|
||||
if (page.displayReadyPromise)
|
||||
page.displayReadyPromise.reject(data.error);
|
||||
else
|
||||
throw data.error;
|
||||
}, this);
|
||||
|
@ -118,7 +118,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
var self = this;
|
||||
var xref = this.xref;
|
||||
var handler = this.handler;
|
||||
var uniquePrefix = this.uniquePrefix;
|
||||
var uniquePrefix = this.uniquePrefix || '';
|
||||
|
||||
function insertDependency(depList) {
|
||||
fnArray.push('dependency');
|
||||
@ -211,7 +211,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
args = [objId, w, h];
|
||||
|
||||
var softMask = dict.get('SMask', 'IM') || false;
|
||||
if (!softMask && image instanceof JpegStream && image.isNative) {
|
||||
if (!softMask && image instanceof JpegStream &&
|
||||
image.isNativelySupported(xref, resources)) {
|
||||
// These JPEGs don't need any more processing so we can just send it.
|
||||
fn = 'paintJpegXObject';
|
||||
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
||||
@ -234,7 +235,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}, handler, xref, resources, image, inline);
|
||||
}
|
||||
|
||||
uniquePrefix = uniquePrefix || '';
|
||||
if (!queue.argsArray) {
|
||||
queue.argsArray = [];
|
||||
}
|
||||
|
@ -2092,7 +2092,7 @@ var Font = (function FontClosure() {
|
||||
window.btoa(data) + ');');
|
||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||
|
||||
document.documentElement.firstChild.appendChild(
|
||||
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
||||
document.createElement('style'));
|
||||
|
||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
|
553
src/function.js
553
src/function.js
@ -270,7 +270,6 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
|
||||
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||
var domain = dict.get('Domain');
|
||||
var range = dict.get('Range');
|
||||
|
||||
if (!domain)
|
||||
error('No domain');
|
||||
@ -279,13 +278,13 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
if (inputSize != 1)
|
||||
error('Bad domain for stiched function');
|
||||
|
||||
var fnRefs = dict.get('Functions');
|
||||
var fnRefs = xref.fetchIfRef(dict.get('Functions'));
|
||||
var fns = [];
|
||||
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||
|
||||
var bounds = dict.get('Bounds');
|
||||
var encode = dict.get('Encode');
|
||||
var bounds = xref.fetchIfRef(dict.get('Bounds'));
|
||||
var encode = xref.fetchIfRef(dict.get('Encode'));
|
||||
|
||||
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||
},
|
||||
@ -336,16 +335,550 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
};
|
||||
},
|
||||
|
||||
constructPostScript: function pdfFunctionConstructPostScript() {
|
||||
return [CONSTRUCT_POSTSCRIPT];
|
||||
constructPostScript: function pdfFunctionConstructPostScript(fn, dict,
|
||||
xref) {
|
||||
var domain = dict.get('Domain');
|
||||
var range = dict.get('Range');
|
||||
|
||||
if (!domain)
|
||||
error('No domain.');
|
||||
|
||||
if (!range)
|
||||
error('No range.');
|
||||
|
||||
var lexer = new PostScriptLexer(fn);
|
||||
var parser = new PostScriptParser(lexer);
|
||||
var code = parser.parse();
|
||||
|
||||
return [CONSTRUCT_POSTSCRIPT, domain, range, code];
|
||||
},
|
||||
|
||||
constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() {
|
||||
TODO('unhandled type of function');
|
||||
return function constructPostScriptFromIRResult() {
|
||||
return [255, 105, 180];
|
||||
constructPostScriptFromIR:
|
||||
function pdfFunctionConstructPostScriptFromIR(IR) {
|
||||
var domain = IR[1];
|
||||
var range = IR[2];
|
||||
var code = IR[3];
|
||||
var numOutputs = range.length / 2;
|
||||
var evaluator = new PostScriptEvaluator(code);
|
||||
// Cache the values for a big speed up, the cache size is limited though
|
||||
// since the number of possible values can be huge from a PS function.
|
||||
var cache = new FunctionCache();
|
||||
return function constructPostScriptFromIRResult(args) {
|
||||
var initialStack = [];
|
||||
for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
|
||||
initialStack.push(args[i]);
|
||||
}
|
||||
|
||||
var key = initialStack.join('_');
|
||||
if (cache.has(key))
|
||||
return cache.get(key);
|
||||
|
||||
var stack = evaluator.execute(initialStack);
|
||||
var transformed = new Array(numOutputs);
|
||||
for (i = numOutputs - 1; i >= 0; --i) {
|
||||
var out = stack.pop();
|
||||
var rangeIndex = 2 * i;
|
||||
if (out < range[rangeIndex])
|
||||
out = range[rangeIndex];
|
||||
else if (out > range[rangeIndex + 1])
|
||||
out = range[rangeIndex + 1];
|
||||
transformed[i] = out;
|
||||
}
|
||||
cache.set(key, transformed);
|
||||
return transformed;
|
||||
};
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var FunctionCache = (function FunctionCacheClosure() {
|
||||
// Of 10 PDF's with type4 functions the maxium number of distinct values seen
|
||||
// was 256. This still may need some tweaking in the future though.
|
||||
var MAX_CACHE_SIZE = 1024;
|
||||
function FunctionCache() {
|
||||
this.cache = {};
|
||||
this.total = 0;
|
||||
}
|
||||
FunctionCache.prototype = {
|
||||
has: function has(key) {
|
||||
return key in this.cache;
|
||||
},
|
||||
get: function get(key) {
|
||||
return this.cache[key];
|
||||
},
|
||||
set: function set(key, value) {
|
||||
if (this.total < MAX_CACHE_SIZE) {
|
||||
this.cache[key] = value;
|
||||
this.total++;
|
||||
}
|
||||
}
|
||||
};
|
||||
return FunctionCache;
|
||||
})();
|
||||
|
||||
var PostScriptStack = (function PostScriptStackClosure() {
|
||||
var MAX_STACK_SIZE = 100;
|
||||
function PostScriptStack(initialStack) {
|
||||
this.stack = initialStack || [];
|
||||
}
|
||||
|
||||
PostScriptStack.prototype = {
|
||||
push: function push(value) {
|
||||
if (this.stack.length >= MAX_STACK_SIZE)
|
||||
error('PostScript function stack overflow.');
|
||||
this.stack.push(value);
|
||||
},
|
||||
pop: function pop() {
|
||||
if (this.stack.length <= 0)
|
||||
error('PostScript function stack underflow.');
|
||||
return this.stack.pop();
|
||||
},
|
||||
copy: function copy(n) {
|
||||
if (this.stack.length + n >= MAX_STACK_SIZE)
|
||||
error('PostScript function stack overflow.');
|
||||
var stack = this.stack;
|
||||
for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
|
||||
stack.push(stack[i]);
|
||||
},
|
||||
index: function index(n) {
|
||||
this.push(this.stack[this.stack.length - n - 1]);
|
||||
},
|
||||
// rotate the last n stack elements p times
|
||||
roll: function roll(n, p) {
|
||||
var stack = this.stack;
|
||||
var l = stack.length - n;
|
||||
var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
|
||||
for (i = l, j = r; i < j; i++, j--) {
|
||||
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||
}
|
||||
for (i = l, j = c - 1; i < j; i++, j--) {
|
||||
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||
}
|
||||
for (i = c, j = r; i < j; i++, j--) {
|
||||
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||
}
|
||||
}
|
||||
};
|
||||
return PostScriptStack;
|
||||
})();
|
||||
var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
|
||||
function PostScriptEvaluator(operators, operands) {
|
||||
this.operators = operators;
|
||||
this.operands = operands;
|
||||
}
|
||||
PostScriptEvaluator.prototype = {
|
||||
execute: function execute(initialStack) {
|
||||
var stack = new PostScriptStack(initialStack);
|
||||
var counter = 0;
|
||||
var operators = this.operators;
|
||||
var length = operators.length;
|
||||
var operator, a, b;
|
||||
while (counter < length) {
|
||||
operator = operators[counter++];
|
||||
if (typeof operator == 'number') {
|
||||
// Operator is really an operand and should be pushed to the stack.
|
||||
stack.push(operator);
|
||||
continue;
|
||||
}
|
||||
switch (operator) {
|
||||
// non standard ps operators
|
||||
case 'jz': // jump if false
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (!a)
|
||||
counter = b;
|
||||
break;
|
||||
case 'j': // jump
|
||||
a = stack.pop();
|
||||
counter = a;
|
||||
break;
|
||||
|
||||
// all ps operators in alphabetical order (excluding if/ifelse)
|
||||
case 'abs':
|
||||
a = stack.pop();
|
||||
stack.push(Math.abs(a));
|
||||
break;
|
||||
case 'add':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a + b);
|
||||
break;
|
||||
case 'and':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (isBool(a) && isBool(b))
|
||||
stack.push(a && b);
|
||||
else
|
||||
stack.push(a & b);
|
||||
break;
|
||||
case 'atan':
|
||||
a = stack.pop();
|
||||
stack.push(Math.atan(a));
|
||||
break;
|
||||
case 'bitshift':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (a > 0)
|
||||
stack.push(a << b);
|
||||
else
|
||||
stack.push(a >> b);
|
||||
break;
|
||||
case 'ceiling':
|
||||
a = stack.pop();
|
||||
stack.push(Math.ceil(a));
|
||||
break;
|
||||
case 'copy':
|
||||
a = stack.pop();
|
||||
stack.copy(a);
|
||||
break;
|
||||
case 'cos':
|
||||
a = stack.pop();
|
||||
stack.push(Math.cos(a));
|
||||
break;
|
||||
case 'cvi':
|
||||
a = stack.pop() | 0;
|
||||
stack.push(a);
|
||||
break;
|
||||
case 'cvr':
|
||||
// noop
|
||||
break;
|
||||
case 'div':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a / b);
|
||||
break;
|
||||
case 'dup':
|
||||
stack.copy(1);
|
||||
break;
|
||||
case 'eq':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a == b);
|
||||
break;
|
||||
case 'exch':
|
||||
stack.roll(2, 1);
|
||||
break;
|
||||
case 'exp':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(Math.pow(a, b));
|
||||
break;
|
||||
case 'false':
|
||||
stack.push(false);
|
||||
break;
|
||||
case 'floor':
|
||||
a = stack.pop();
|
||||
stack.push(Math.floor(a));
|
||||
break;
|
||||
case 'ge':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a >= b);
|
||||
break;
|
||||
case 'gt':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a > b);
|
||||
break;
|
||||
case 'idiv':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push((a / b) | 0);
|
||||
break;
|
||||
case 'index':
|
||||
a = stack.pop();
|
||||
stack.index(a);
|
||||
break;
|
||||
case 'le':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a <= b);
|
||||
break;
|
||||
case 'ln':
|
||||
a = stack.pop();
|
||||
stack.push(Math.log(a));
|
||||
break;
|
||||
case 'log':
|
||||
a = stack.pop();
|
||||
stack.push(Math.log(a) / Math.LN10);
|
||||
break;
|
||||
case 'lt':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a < b);
|
||||
break;
|
||||
case 'mod':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a % b);
|
||||
break;
|
||||
case 'mul':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a * b);
|
||||
break;
|
||||
case 'ne':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a != b);
|
||||
break;
|
||||
case 'neg':
|
||||
a = stack.pop();
|
||||
stack.push(-b);
|
||||
break;
|
||||
case 'not':
|
||||
a = stack.pop();
|
||||
if (isBool(a) && isBool(b))
|
||||
stack.push(a && b);
|
||||
else
|
||||
stack.push(a & b);
|
||||
break;
|
||||
case 'or':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (isBool(a) && isBool(b))
|
||||
stack.push(a || b);
|
||||
else
|
||||
stack.push(a | b);
|
||||
break;
|
||||
case 'pop':
|
||||
stack.pop();
|
||||
break;
|
||||
case 'roll':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.roll(a, b);
|
||||
break;
|
||||
case 'round':
|
||||
a = stack.pop();
|
||||
stack.push(Math.round(a));
|
||||
break;
|
||||
case 'sin':
|
||||
a = stack.pop();
|
||||
stack.push(Math.sin(a));
|
||||
break;
|
||||
case 'sqrt':
|
||||
a = stack.pop();
|
||||
stack.push(Math.sqrt(a));
|
||||
break;
|
||||
case 'sub':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a - b);
|
||||
break;
|
||||
case 'true':
|
||||
stack.push(true);
|
||||
break;
|
||||
case 'truncate':
|
||||
a = stack.pop();
|
||||
a = a < 0 ? Math.ceil(a) : Math.floor(a);
|
||||
stack.push(a);
|
||||
break;
|
||||
case 'xor':
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (isBool(a) && isBool(b))
|
||||
stack.push(a != b);
|
||||
else
|
||||
stack.push(a ^ b);
|
||||
break;
|
||||
default:
|
||||
error('Unknown operator ' + operator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return stack.stack;
|
||||
}
|
||||
};
|
||||
return PostScriptEvaluator;
|
||||
})();
|
||||
|
||||
var PostScriptParser = (function PostScriptParserClosure() {
|
||||
function PostScriptParser(lexer) {
|
||||
this.lexer = lexer;
|
||||
this.operators = [];
|
||||
this.token;
|
||||
this.prev;
|
||||
}
|
||||
PostScriptParser.prototype = {
|
||||
nextToken: function nextToken() {
|
||||
this.prev = this.token;
|
||||
this.token = this.lexer.getToken();
|
||||
},
|
||||
accept: function accept(type) {
|
||||
if (this.token.type == type) {
|
||||
this.nextToken();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
expect: function expect(type) {
|
||||
if (this.accept(type))
|
||||
return true;
|
||||
error('Unexpected symbol: found ' + this.token.type + ' expected ' +
|
||||
type + '.');
|
||||
},
|
||||
parse: function parse() {
|
||||
this.nextToken();
|
||||
this.expect(PostScriptTokenTypes.LBRACE);
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
return this.operators;
|
||||
},
|
||||
parseBlock: function parseBlock() {
|
||||
while (true) {
|
||||
if (this.accept(PostScriptTokenTypes.NUMBER)) {
|
||||
this.operators.push(this.prev.value);
|
||||
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
|
||||
this.operators.push(this.prev.value);
|
||||
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||
this.parseCondition();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
parseCondition: function parseCondition() {
|
||||
// Add two place holders that will be updated later
|
||||
var conditionLocation = this.operators.length;
|
||||
this.operators.push(null, null);
|
||||
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
if (this.accept(PostScriptTokenTypes.IF)) {
|
||||
// The true block is right after the 'if' so it just falls through on
|
||||
// true else it jumps and skips the true block.
|
||||
this.operators[conditionLocation] = this.operators.length;
|
||||
this.operators[conditionLocation + 1] = 'jz';
|
||||
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||
var jumpLocation = this.operators.length;
|
||||
this.operators.push(null, null);
|
||||
var endOfTrue = this.operators.length;
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
this.expect(PostScriptTokenTypes.IFELSE);
|
||||
// The jump is added at the end of the true block to skip the false
|
||||
// block.
|
||||
this.operators[jumpLocation] = this.operators.length;
|
||||
this.operators[jumpLocation + 1] = 'j';
|
||||
|
||||
this.operators[conditionLocation] = endOfTrue;
|
||||
this.operators[conditionLocation + 1] = 'jz';
|
||||
} else {
|
||||
error('PS Function: error parsing conditional.');
|
||||
}
|
||||
}
|
||||
};
|
||||
return PostScriptParser;
|
||||
})();
|
||||
|
||||
var PostScriptTokenTypes = {
|
||||
LBRACE: 0,
|
||||
RBRACE: 1,
|
||||
NUMBER: 2,
|
||||
OPERATOR: 3,
|
||||
IF: 4,
|
||||
IFELSE: 5
|
||||
};
|
||||
|
||||
var PostScriptToken = (function PostScriptTokenClosure() {
|
||||
function PostScriptToken(type, value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
var opCache = {};
|
||||
|
||||
PostScriptToken.getOperator = function getOperator(op) {
|
||||
var opValue = opCache[op];
|
||||
if (opValue)
|
||||
return opValue;
|
||||
|
||||
return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
|
||||
};
|
||||
|
||||
PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
|
||||
'{');
|
||||
PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
|
||||
'}');
|
||||
PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
|
||||
PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
|
||||
'IFELSE');
|
||||
return PostScriptToken;
|
||||
})();
|
||||
|
||||
var PostScriptLexer = (function PostScriptLexerClosure() {
|
||||
function PostScriptLexer(stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
PostScriptLexer.prototype = {
|
||||
getToken: function getToken() {
|
||||
var s = '';
|
||||
var ch;
|
||||
var comment = false;
|
||||
var stream = this.stream;
|
||||
|
||||
// skip comments
|
||||
while (true) {
|
||||
if (!(ch = stream.getChar()))
|
||||
return EOF;
|
||||
|
||||
if (comment) {
|
||||
if (ch == '\x0a' || ch == '\x0d')
|
||||
comment = false;
|
||||
} else if (ch == '%') {
|
||||
comment = true;
|
||||
} else if (!Lexer.isSpace(ch)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (ch) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '+': case '-': case '.':
|
||||
return new PostScriptToken(PostScriptTokenTypes.NUMBER,
|
||||
this.getNumber(ch));
|
||||
case '{':
|
||||
return PostScriptToken.LBRACE;
|
||||
case '}':
|
||||
return PostScriptToken.RBRACE;
|
||||
}
|
||||
// operator
|
||||
var str = ch.toLowerCase();
|
||||
while (true) {
|
||||
ch = stream.lookChar().toLowerCase();
|
||||
if (ch >= 'a' && ch <= 'z')
|
||||
str += ch;
|
||||
else
|
||||
break;
|
||||
stream.skip();
|
||||
}
|
||||
switch (str) {
|
||||
case 'if':
|
||||
return PostScriptToken.IF;
|
||||
case 'ifelse':
|
||||
return PostScriptToken.IFELSE;
|
||||
default:
|
||||
return PostScriptToken.getOperator(str);
|
||||
}
|
||||
},
|
||||
getNumber: function getNumber(ch) {
|
||||
var str = ch;
|
||||
var stream = this.stream;
|
||||
while (true) {
|
||||
ch = stream.lookChar();
|
||||
if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.')
|
||||
str += ch;
|
||||
else
|
||||
break;
|
||||
stream.skip();
|
||||
}
|
||||
var value = parseFloat(str);
|
||||
if (isNaN(value))
|
||||
error('Invalid floating point number: ' + value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
return PostScriptLexer;
|
||||
})();
|
||||
|
||||
|
@ -9,7 +9,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
* when the image data is ready.
|
||||
*/
|
||||
function handleImageData(handler, xref, res, image, promise) {
|
||||
if (image instanceof JpegStream && image.isNative) {
|
||||
if (image instanceof JpegStream && image.isNativelyDecodable(xref, res)) {
|
||||
// For natively supported jpegs send them to the main thread for decoding.
|
||||
var dict = image.dict;
|
||||
var colorSpace = dict.get('ColorSpace', 'CS');
|
||||
|
34
src/obj.js
34
src/obj.js
@ -8,8 +8,7 @@ var Name = (function NameClosure() {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Name.prototype = {
|
||||
};
|
||||
Name.prototype = {};
|
||||
|
||||
return Name;
|
||||
})();
|
||||
@ -19,9 +18,7 @@ var Cmd = (function CmdClosure() {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
Cmd.prototype = {
|
||||
};
|
||||
|
||||
Cmd.prototype = {};
|
||||
|
||||
var cmdCache = {};
|
||||
|
||||
@ -80,8 +77,7 @@ var Ref = (function RefClosure() {
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
Ref.prototype = {
|
||||
};
|
||||
Ref.prototype = {};
|
||||
|
||||
return Ref;
|
||||
})();
|
||||
@ -124,11 +120,11 @@ var Catalog = (function CatalogClosure() {
|
||||
return shadow(this, 'toplevelPagesDict', xrefObj);
|
||||
},
|
||||
get documentOutline() {
|
||||
var obj = this.catDict.get('Outlines');
|
||||
var xref = this.xref;
|
||||
var obj = xref.fetchIfRef(this.catDict.get('Outlines'));
|
||||
var root = { items: [] };
|
||||
if (isRef(obj)) {
|
||||
obj = xref.fetch(obj).get('First');
|
||||
if (isDict(obj)) {
|
||||
obj = obj.get('First');
|
||||
var processed = new RefSet();
|
||||
if (isRef(obj)) {
|
||||
var queue = [{obj: obj, parent: root}];
|
||||
@ -273,7 +269,7 @@ var XRef = (function XRefClosure() {
|
||||
this.entries = [];
|
||||
this.xrefstms = {};
|
||||
var trailerDict = this.readXRef(startXRef);
|
||||
|
||||
this.trailer = trailerDict;
|
||||
// prepare the XRef cache
|
||||
this.cache = [];
|
||||
|
||||
@ -556,9 +552,7 @@ var XRef = (function XRefClosure() {
|
||||
},
|
||||
getEntry: function xRefGetEntry(i) {
|
||||
var e = this.entries[i];
|
||||
if (e.free)
|
||||
error('reading an XRef stream not implemented yet');
|
||||
return e;
|
||||
return e.free ? null : e; // returns null is the entry is free
|
||||
},
|
||||
fetchIfRef: function xRefFetchIfRef(obj) {
|
||||
if (!isRef(obj))
|
||||
@ -567,11 +561,15 @@ var XRef = (function XRefClosure() {
|
||||
},
|
||||
fetch: function xRefFetch(ref, suppressEncryption) {
|
||||
var num = ref.num;
|
||||
var e = this.cache[num];
|
||||
if (e)
|
||||
return e;
|
||||
if (num in this.cache)
|
||||
return this.cache[num];
|
||||
|
||||
var e = this.getEntry(num);
|
||||
|
||||
// the referenced entry can be free
|
||||
if (e === null)
|
||||
return (this.cache[num] = e);
|
||||
|
||||
e = this.getEntry(num);
|
||||
var gen = ref.gen;
|
||||
var stream, parser;
|
||||
if (e.uncompressed) {
|
||||
|
@ -803,29 +803,16 @@ var JpegStream = (function JpegStreamClosure() {
|
||||
// need to be removed
|
||||
this.dict = dict;
|
||||
|
||||
// Flag indicating wether the image can be natively loaded.
|
||||
this.isNative = true;
|
||||
|
||||
this.colorTransform = -1;
|
||||
this.isAdobeImage = false;
|
||||
this.colorTransform = dict.get('ColorTransform') || -1;
|
||||
|
||||
if (isAdobeImage(bytes)) {
|
||||
// when bug 674619 land, let's check if browser can do
|
||||
// normal cmyk and then we won't have to the following
|
||||
var cs = xref.fetchIfRef(dict.get('ColorSpace'));
|
||||
|
||||
// DeviceRGB and DeviceGray are the only Adobe images that work natively
|
||||
if (isName(cs) && (cs.name === 'DeviceRGB' || cs.name === 'DeviceGray')) {
|
||||
bytes = fixAdobeImage(bytes);
|
||||
this.src = bytesToString(bytes);
|
||||
} else {
|
||||
this.colorTransform = dict.get('ColorTransform');
|
||||
this.isNative = false;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
} else {
|
||||
this.src = bytesToString(bytes);
|
||||
this.isAdobeImage = true;
|
||||
bytes = fixAdobeImage(bytes);
|
||||
}
|
||||
|
||||
this.bytes = bytes;
|
||||
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
@ -835,7 +822,8 @@ var JpegStream = (function JpegStreamClosure() {
|
||||
if (this.bufferLength)
|
||||
return;
|
||||
var jpegImage = new JpegImage();
|
||||
jpegImage.colorTransform = this.colorTransform;
|
||||
if (this.colorTransform != -1)
|
||||
jpegImage.colorTransform = this.colorTransform;
|
||||
jpegImage.parse(this.bytes);
|
||||
var width = jpegImage.width;
|
||||
var height = jpegImage.height;
|
||||
@ -844,11 +832,39 @@ var JpegStream = (function JpegStreamClosure() {
|
||||
this.bufferLength = data.length;
|
||||
};
|
||||
JpegStream.prototype.getIR = function jpegStreamGetIR() {
|
||||
return this.src;
|
||||
return bytesToString(this.bytes);
|
||||
};
|
||||
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
||||
error('internal error: getChar is not valid on JpegStream');
|
||||
};
|
||||
/**
|
||||
* Checks if the image can be decoded and displayed by the browser without any
|
||||
* further processing such as color space conversions.
|
||||
*/
|
||||
JpegStream.prototype.isNativelySupported = function isNativelySupported(xref,
|
||||
res) {
|
||||
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
|
||||
// when bug 674619 lands, let's check if browser can do
|
||||
// normal cmyk and then we won't need to decode in JS
|
||||
if (cs.name === 'DeviceGray' || cs.name === 'DeviceRGB')
|
||||
return true;
|
||||
if (cs.name === 'DeviceCMYK' && !this.isAdobeImage &&
|
||||
this.colorTransform < 1)
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* Checks if the image can be decoded by the browser.
|
||||
*/
|
||||
JpegStream.prototype.isNativelyDecodable = function isNativelyDecodable(xref,
|
||||
res) {
|
||||
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
|
||||
var numComps = cs.numComps;
|
||||
if (numComps == 1 || numComps == 3)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return JpegStream;
|
||||
})();
|
||||
@ -1856,10 +1872,10 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
// values. The first array element indicates whether a valid code is being
|
||||
// returned. The second array element is the actual code. The third array
|
||||
// element indicates whether EOF was reached.
|
||||
var findTableCode = function ccittFaxStreamFindTableCode(start, end, table,
|
||||
limit) {
|
||||
var limitValue = limit || 0;
|
||||
CCITTFaxStream.prototype.findTableCode =
|
||||
function ccittFaxStreamFindTableCode(start, end, table, limit) {
|
||||
|
||||
var limitValue = limit || 0;
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var code = this.lookBits(i);
|
||||
if (code == EOF)
|
||||
@ -1890,7 +1906,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(1, 7, twoDimTable);
|
||||
var result = this.findTableCode(1, 7, twoDimTable);
|
||||
if (result[0] && result[2])
|
||||
return result[1];
|
||||
}
|
||||
@ -1919,11 +1935,11 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(1, 9, whiteTable2);
|
||||
var result = this.findTableCode(1, 9, whiteTable2);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(11, 12, whiteTable1);
|
||||
result = this.findTableCode(11, 12, whiteTable1);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
}
|
||||
@ -1952,15 +1968,15 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(2, 6, blackTable3);
|
||||
var result = this.findTableCode(2, 6, blackTable3);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(7, 12, blackTable2, 64);
|
||||
result = this.findTableCode(7, 12, blackTable2, 64);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(10, 13, blackTable1);
|
||||
result = this.findTableCode(10, 13, blackTable1);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
}
|
||||
|
32
src/util.js
32
src/util.js
@ -206,6 +206,8 @@ var Promise = (function PromiseClosure() {
|
||||
*/
|
||||
function Promise(name, data) {
|
||||
this.name = name;
|
||||
this.isRejected = false;
|
||||
this.error = null;
|
||||
// If you build a promise and pass in some data it's already resolved.
|
||||
if (data != null) {
|
||||
this.isResolved = true;
|
||||
@ -216,6 +218,7 @@ var Promise = (function PromiseClosure() {
|
||||
this._data = EMPTY_PROMISE;
|
||||
}
|
||||
this.callbacks = [];
|
||||
this.errbacks = [];
|
||||
};
|
||||
/**
|
||||
* Builds a promise that is resolved when all the passed in promises are
|
||||
@ -282,9 +285,12 @@ var Promise = (function PromiseClosure() {
|
||||
if (this.isResolved) {
|
||||
throw 'A Promise can be resolved only once ' + this.name;
|
||||
}
|
||||
if (this.isRejected) {
|
||||
throw 'The Promise was already rejected ' + this.name;
|
||||
}
|
||||
|
||||
this.isResolved = true;
|
||||
this.data = data;
|
||||
this.data = data || null;
|
||||
var callbacks = this.callbacks;
|
||||
|
||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||
@ -292,7 +298,24 @@ var Promise = (function PromiseClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
then: function promiseThen(callback) {
|
||||
reject: function proimseReject(reason) {
|
||||
if (this.isRejected) {
|
||||
throw 'A Promise can be rejected only once ' + this.name;
|
||||
}
|
||||
if (this.isResolved) {
|
||||
throw 'The Promise was already resolved ' + this.name;
|
||||
}
|
||||
|
||||
this.isRejected = true;
|
||||
this.error = reason || null;
|
||||
var errbacks = this.errbacks;
|
||||
|
||||
for (var i = 0, ii = errbacks.length; i < ii; i++) {
|
||||
errbacks[i].call(null, reason);
|
||||
}
|
||||
},
|
||||
|
||||
then: function promiseThen(callback, errback) {
|
||||
if (!callback) {
|
||||
throw 'Requiring callback' + this.name;
|
||||
}
|
||||
@ -301,8 +324,13 @@ var Promise = (function PromiseClosure() {
|
||||
if (this.isResolved) {
|
||||
var data = this.data;
|
||||
callback.call(null, data);
|
||||
} else if (this.isRejected && errorback) {
|
||||
var error = this.error;
|
||||
errback.call(null, error);
|
||||
} else {
|
||||
this.callbacks.push(callback);
|
||||
if (errback)
|
||||
this.errbacks.push(errback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -165,9 +165,14 @@ function nextPage(task, loadError) {
|
||||
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
||||
clear(ctx);
|
||||
|
||||
// using non-attached to the document div to test
|
||||
// using the text layer builder that does nothing to test
|
||||
// text layer creation operations
|
||||
var textLayer = document.createElement('div');
|
||||
var textLayerBuilder = {
|
||||
beginLayout: function nullTextLayerBuilderBeginLayout() {},
|
||||
endLayout: function nullTextLayerBuilderEndLayout() {},
|
||||
appendText: function nullTextLayerBuilderAppendText(text, fontName,
|
||||
fontSize) {}
|
||||
};
|
||||
|
||||
page.startRendering(
|
||||
ctx,
|
||||
@ -177,7 +182,7 @@ function nextPage(task, loadError) {
|
||||
failureMessage = 'render : ' + error.message;
|
||||
snapshotCurrentPage(task, failureMessage);
|
||||
},
|
||||
textLayer
|
||||
textLayerBuilder
|
||||
);
|
||||
} catch (e) {
|
||||
failure = 'page setup : ' + e.toString();
|
||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -21,3 +21,4 @@
|
||||
!freeculture.pdf
|
||||
!issue918.pdf
|
||||
!smaskdim.pdf
|
||||
!type4psfunc.pdf
|
||||
|
@ -1 +1 @@
|
||||
http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures-software-developer-vol-1-manual.pdf
|
||||
http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf
|
1
test/pdfs/issue1001.pdf.link
Normal file
1
test/pdfs/issue1001.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.myhillsapartment.com/island_club/floorplans/images/links/Island_IC_brochure.pdf
|
1
test/pdfs/issue1015.pdf.link
Normal file
1
test/pdfs/issue1015.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://faculty.washington.edu/fidelr/RayaPubs/TheCaseStudyMethod.pdf
|
1
test/pdfs/ocs.pdf.link
Normal file
1
test/pdfs/ocs.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.unibuc.ro/uploads_en/29535/10/Cyrillic_Alphabets-Chars.pdf
|
BIN
test/pdfs/type4psfunc.pdf
Executable file
BIN
test/pdfs/type4psfunc.pdf
Executable file
Binary file not shown.
1
test/resources/firefox/prefs.js
Normal file
1
test/resources/firefox/prefs.js
Normal file
@ -0,0 +1 @@
|
||||
user_pref("browser.shell.checkDefaultBrowser", false);
|
@ -17,12 +17,13 @@
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "intelisa-load",
|
||||
{ "id": "intelisa-eq",
|
||||
"file": "pdfs/intelisa.pdf",
|
||||
"md5": "f5712097d29287a97f1278839814f682",
|
||||
"md5": "83032ccbfdc5a66269b1403971110abe",
|
||||
"link": true,
|
||||
"pageLimit": 100,
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "pdfspec-load",
|
||||
"file": "pdfs/pdf.pdf",
|
||||
@ -355,6 +356,13 @@
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1001",
|
||||
"file": "pdfs/issue1001.pdf",
|
||||
"md5": "0f1496e80a82a923e91d9e74c55ad94e",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "aboutstacks",
|
||||
"file": "pdfs/aboutstacks.pdf",
|
||||
"md5": "6e7c8416a293ba2d83bc8dd20c6ccf51",
|
||||
@ -367,5 +375,25 @@
|
||||
"md5": "de80aeca7cbf79940189fd34d59671ee",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "type4psfunc",
|
||||
"file": "pdfs/type4psfunc.pdf",
|
||||
"md5": "7e6027a02ff78577f74dccdf84e37189",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "ocs",
|
||||
"file": "pdfs/ocs.pdf",
|
||||
"md5": "2ade57e954ae7632749cf328daeaa7a8",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "issue1015",
|
||||
"file": "pdfs/issue1015.pdf",
|
||||
"md5": "b61503d1b445742b665212866afb60e2",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
}
|
||||
]
|
||||
|
116
test/unit/Makefile
Normal file
116
test/unit/Makefile
Normal file
@ -0,0 +1,116 @@
|
||||
# Create temporary profile directory name.
|
||||
TEMP_PROFILE:=$(shell echo `pwd`)/test_reports/temp_profile
|
||||
|
||||
# These are the Firefox command line arguments.
|
||||
FIREFOX_ARGS:=-no-remote -profile $(TEMP_PROFILE)
|
||||
|
||||
# These are the Chrome command line arguments.
|
||||
CHROME_ARGS:=--user-data-dir=$(TEMP_PROFILE) --no-first-run --disable-sync
|
||||
|
||||
# Unit test uses the manifest from ref test to determine which browsers will
|
||||
# be used for running the unit tests.
|
||||
MANIFEST:=../resources/browser_manifests/browser_manifest.json
|
||||
|
||||
# This is a helper command to separate multiple browsers to their own lines
|
||||
# for an easier sed operation.
|
||||
SPLIT_LINES:=sed 's|,|,@|g' | tr '@' '\n'
|
||||
|
||||
# This is a helper command to join multiple lines together.
|
||||
JOIN_LINES:=tr -d '\n'
|
||||
|
||||
# Fetch the paths to browsers that are going to be used in testing.
|
||||
# For OS X the path to the binary needs to be added.
|
||||
# Add the browser arguments for each browser.
|
||||
# Create random profile directory for each browser.
|
||||
BROWSERS_PATHS:=$(shell echo `\
|
||||
sed -n 's|.*"path":\(.*\)|\1,|p' $(MANIFEST) | \
|
||||
$(JOIN_LINES) \
|
||||
`)
|
||||
|
||||
# The browser_manifest.json file has only the app directory for mac browsers.
|
||||
# The absolute path to the browser binary needs to be used.
|
||||
BROWSERS_PATHS_WITH_MAC_CORRECTION:=$(shell echo '$(BROWSERS_PATHS)' | \
|
||||
$(SPLIT_LINES) | \
|
||||
sed 's|\(Aurora\.app\)|\1/Contents/MacOS/firefox-bin|' | \
|
||||
sed 's|\(Firefox.*\.app\)|\1/Contents/MacOS/firefox-bin|' | \
|
||||
sed 's|\(Google Chrome\.app\)|\1/Contents/MacOS/Google Chrome|' | \
|
||||
$(JOIN_LINES) \
|
||||
)
|
||||
|
||||
# Replace " with @@@@ so that echoing do not destroy the quotation marks.
|
||||
QUOTATION_MARK:=\"
|
||||
SUBSTITUTE_FOR_QUOTATION_MARK:=@@@@
|
||||
|
||||
# Each of the browser can have their own separate arguments.
|
||||
BROWSERS_WITH_ARGUMENTS:=$(shell echo '$(BROWSERS_PATHS_WITH_MAC_CORRECTION)' | \
|
||||
$(SPLIT_LINES) | \
|
||||
sed "s|\(irefox.*\)\($(QUOTATION_MARK)\),|\1;$(FIREFOX_ARGS)\2,|" | \
|
||||
sed "s|\(hrome.*\)\($(QUOTATION_MARK)\),|\1;$(CHROME_ARGS)\2,|" | \
|
||||
$(JOIN_LINES) \
|
||||
)
|
||||
|
||||
# A temporary profile directory is needed for each of the browser. In this way
|
||||
# a unit test run will not disturb the main browsing session of the user. The
|
||||
# $RANDOM shell variable is used to generate non-conflicting temporary
|
||||
# directories.
|
||||
BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS:=$(shell echo '$(BROWSERS_WITH_ARGUMENTS)' | \
|
||||
$(SPLIT_LINES) | \
|
||||
sed 's|\(temp_profile\)|\1_$$RANDOM$$RANDOM|' | \
|
||||
sed "s|$(QUOTATION_MARK)|$(SUBSTITUTE_FOR_QUOTATION_MARK)|g" | \
|
||||
$(JOIN_LINES) \
|
||||
)
|
||||
|
||||
# Echo the variable so that the unknown random directories become known.
|
||||
# Replace @@@@ with " so that jsTestDriver will work properly.
|
||||
BROWSERS:=$(shell echo "$(BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS)" | \
|
||||
sed "s|$(SUBSTITUTE_FOR_QUOTATION_MARK)|$(QUOTATION_MARK)|g" \
|
||||
)
|
||||
|
||||
# Get the known random directories for browsers. This information will be used
|
||||
# to create the profile directories beforehand. Create also the dummy temp
|
||||
# profile directory so that the mkdir command would not fail for browsers that
|
||||
# do not need it.
|
||||
PROFILES:=$(TEMP_PROFILE) $(shell echo '$(BROWSERS)' | \
|
||||
$(SPLIT_LINES) | \
|
||||
sed -n "s|.*\( $(TEMP_PROFILE)_[0-9]\+\).*|\1|p" | \
|
||||
$(JOIN_LINES) \
|
||||
)
|
||||
|
||||
# This is the command to invoke the unit test.
|
||||
PROG:=java \
|
||||
-Xms512m \
|
||||
-Xmx1024m \
|
||||
-jar ../../external/jsTestDriver/JsTestDriver-1.3.3d.jar \
|
||||
--config ./jsTestDriver.conf \
|
||||
--reset \
|
||||
--port 4224 \
|
||||
--browser $(BROWSERS) \
|
||||
--tests all \
|
||||
--testOutput ./test_reports/
|
||||
|
||||
# This default rule runs the unit tests with the constructed command.
|
||||
test:
|
||||
@mkdir -p $(PROFILES)
|
||||
$(PROG)
|
||||
@rm -rf $(PROFILES)
|
||||
|
||||
# In case this Makefile needs to be debugged then this rule will provide all
|
||||
# the information from intermediate steps.
|
||||
debug:
|
||||
@echo 'Debug browsers paths: $(BROWSERS_PATHS)'
|
||||
@echo
|
||||
@echo 'Debug browsers paths with mac correction: $(BROWSERS_PATHS_WITH_MAC_CORRECTION)'
|
||||
@echo
|
||||
@echo 'Debug browsers with arguments: $(BROWSERS_WITH_ARGUMENTS)'
|
||||
@echo
|
||||
@echo 'Debug browsers random profile paths: $(BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS)'
|
||||
@echo
|
||||
@echo 'Debug browsers: $(BROWSERS)'
|
||||
@echo
|
||||
@echo 'Debug profiles: $(PROFILES)'
|
||||
@echo
|
||||
@echo 'Command to be run: $(PROG)'
|
||||
@echo
|
||||
|
||||
.phony:: test
|
||||
|
187
test/unit/crypto_spec.js
Normal file
187
test/unit/crypto_spec.js
Normal file
@ -0,0 +1,187 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('crypto', function() {
|
||||
function string2binary(s) {
|
||||
var n = s.length, i;
|
||||
var result = new Uint8Array(n);
|
||||
for (i = 0; i < n; ++i)
|
||||
result[i] = s.charCodeAt(i) % 0xFF;
|
||||
return result;
|
||||
}
|
||||
|
||||
function hex2binary(s) {
|
||||
var digits = '0123456789ABCDEF';
|
||||
s = s.toUpperCase();
|
||||
var n = s.length >> 1, i, j;
|
||||
var result = new Uint8Array(n);
|
||||
for (i = 0, j = 0; i < n; ++i) {
|
||||
var d1 = s.charAt(j++);
|
||||
var d2 = s.charAt(j++);
|
||||
var value = (digits.indexOf(d1) << 4) | (digits.indexOf(d2));
|
||||
result[i] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// RFC 1321, A.5 Test suite
|
||||
describe('calculateMD5', function() {
|
||||
it('should pass RFC 1321 test #1', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('d41d8cd98f00b204e9800998ecf8427e');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #2', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('a');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('0cc175b9c0f1b6a831c399e269772661');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #3', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('abc');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('900150983cd24fb0d6963f7d28e17f72');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #4', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('message digest');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('f96b697d7cb7938d525a2f31aaf161d0');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #5', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('abcdefghijklmnopqrstuvwxyz');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('c3fcd3d76192e4007dfb496cca67e13b');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #6', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv' +
|
||||
'wxyz0123456789');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('d174ab98d277d9f5a5611c2c9f419d9f');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass RFC 1321 test #7', function() {
|
||||
var input, result, expected;
|
||||
input = string2binary('123456789012345678901234567890123456789012345678' +
|
||||
'90123456789012345678901234567890');
|
||||
result = calculateMD5(input, 0, input.length);
|
||||
expected = hex2binary('57edf4a22be3c955ac49da2e2107b67a');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
// http://www.freemedialibrary.com/index.php/RC4_test_vectors are used
|
||||
describe('ARCFourCipher', function() {
|
||||
it('should pass test #1', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('0123456789abcdef');
|
||||
input = hex2binary('0123456789abcdef');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('75b7878099e0c596');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #2', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('0123456789abcdef');
|
||||
input = hex2binary('0000000000000000');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('7494c2e7104b0879');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #3', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('0000000000000000');
|
||||
input = hex2binary('0000000000000000');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('de188941a3375d3a');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #4', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('ef012345');
|
||||
input = hex2binary('00000000000000000000');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('d6a141a7ec3c38dfbd61');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #5', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('0123456789abcdef');
|
||||
input = hex2binary('010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'10101010101010101010101010101010101010101010101010101010101010101010' +
|
||||
'101010101010101010101');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76' +
|
||||
'533449b6778dcad8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b' +
|
||||
'1b13b6b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377' +
|
||||
'108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d' +
|
||||
'7a4303dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747' +
|
||||
'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f5d44' +
|
||||
'c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421d43df9b4' +
|
||||
'2e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5585cb009290e' +
|
||||
'2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb272912426445998514c1' +
|
||||
'5d53a18c864ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405' +
|
||||
'b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8d31509' +
|
||||
'eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c822d4b8c569d849' +
|
||||
'aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c4bad9ba24b96abf924372c' +
|
||||
'8a8fffb10d55354900a77a3db5f205e1b99fcd8660863a159ad4abe40fa48934163d' +
|
||||
'dde542a6585540fd683cbfd8c00f12129a284deacc4cdefe58be7137541c047126c8' +
|
||||
'd49e2755ab181ab7e940b0c0');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #6', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('fb029e3031323334');
|
||||
input = hex2binary('aaaa0300000008004500004e661a00008011be640a0001220af' +
|
||||
'fffff00890089003a000080a601100001000000000000204543454a4548454346434' +
|
||||
'550464545494546464343414341434143414341414100002000011bd0b604');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('f69c5806bd6ce84626bcbefb9474650aad1f7909b0f64d5f' +
|
||||
'58a503a258b7ed22eb0ea64930d3a056a55742fcce141d485f8aa836dea18df42c53' +
|
||||
'80805ad0c61a5d6f58f41040b24b7d1a693856ed0d4398e7aee3bf0e2a2ca8f7');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
it('should pass test #7', function() {
|
||||
var key, input, result, expected, cipher;
|
||||
key = hex2binary('0123456789abcdef');
|
||||
input = hex2binary('123456789abcdef0123456789abcdef0123456789abcdef0123' +
|
||||
'45678');
|
||||
cipher = new ARCFourCipher(key);
|
||||
result = cipher.encryptBlock(input);
|
||||
expected = hex2binary('66a0949f8af7d6891f7f832ba833c00c892ebe30143ce287' +
|
||||
'40011ecf');
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
225
test/unit/function_spec.js
Normal file
225
test/unit/function_spec.js
Normal file
@ -0,0 +1,225 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('function', function() {
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toMatchArray: function(expected) {
|
||||
var actual = this.actual;
|
||||
if (actual.length != expected.length)
|
||||
return false;
|
||||
for (var i = 0; i < expected.length; i++) {
|
||||
var a = actual[i], b = expected[i];
|
||||
if (isArray(b)) {
|
||||
if (a.length != b.length)
|
||||
return false;
|
||||
for (var j = 0; j < a.length; j++) {
|
||||
var suba = a[j], subb = b[j];
|
||||
if (suba !== subb)
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (a !== b)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('PostScriptParser', function() {
|
||||
function parse(program) {
|
||||
var stream = new StringStream(program);
|
||||
var parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||
return parser.parse();
|
||||
}
|
||||
it('parses empty programs', function() {
|
||||
var output = parse('{}');
|
||||
expect(output.length).toEqual(0);
|
||||
});
|
||||
it('parses positive numbers', function() {
|
||||
var number = 999;
|
||||
var program = parse('{ ' + number + ' }');
|
||||
var expectedProgram = [number];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('parses negative numbers', function() {
|
||||
var number = -999;
|
||||
var program = parse('{ ' + number + ' }');
|
||||
var expectedProgram = [number];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('parses negative floats', function() {
|
||||
var number = 3.3;
|
||||
var program = parse('{ ' + number + ' }');
|
||||
var expectedProgram = [number];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('parses operators', function() {
|
||||
var program = parse('{ sub }');
|
||||
var expectedProgram = ['sub'];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('parses if statements', function() {
|
||||
var program = parse('{ { 99 } if }');
|
||||
var expectedProgram = [3, 'jz', 99];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('parses ifelse statements', function() {
|
||||
var program = parse('{ { 99 } { 44 } ifelse }');
|
||||
var expectedProgram = [5, 'jz', 99, 6, 'j', 44];
|
||||
expect(program).toMatchArray(expectedProgram);
|
||||
});
|
||||
it('handles missing brackets', function() {
|
||||
expect(function() { parse('{'); }).toThrow(
|
||||
new Error('Unexpected symbol: found undefined expected 1.'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('PostScriptEvaluator', function() {
|
||||
function evaluate(program) {
|
||||
var stream = new StringStream(program);
|
||||
var parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||
var code = parser.parse();
|
||||
var evaluator = new PostScriptEvaluator(code);
|
||||
var output = evaluator.execute();
|
||||
return output;
|
||||
}
|
||||
|
||||
it('pushes stack', function() {
|
||||
var stack = evaluate('{ 99 }');
|
||||
var expectedStack = [99];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('handles if with true', function() {
|
||||
var stack = evaluate('{ 1 {99} if }');
|
||||
var expectedStack = [99];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('handles if with false', function() {
|
||||
var stack = evaluate('{ 0 {99} if }');
|
||||
var expectedStack = [];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('handles ifelse with true', function() {
|
||||
var stack = evaluate('{ 1 {99} {77} ifelse }');
|
||||
var expectedStack = [99];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('handles ifelse with false', function() {
|
||||
var stack = evaluate('{ 0 {99} {77} ifelse }');
|
||||
var expectedStack = [77];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('handles nested if', function() {
|
||||
var stack = evaluate('{ 1 {1 {77} if} if }');
|
||||
var expectedStack = [77];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
|
||||
it('abs', function() {
|
||||
var stack = evaluate('{ -2 abs }');
|
||||
var expectedStack = [2];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('adds', function() {
|
||||
var stack = evaluate('{ 1 2 add }');
|
||||
var expectedStack = [3];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('boolean ands', function() {
|
||||
var stack = evaluate('{ true false and }');
|
||||
var expectedStack = [false];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('bitwise ands', function() {
|
||||
var stack = evaluate('{ 254 1 and }');
|
||||
var expectedStack = [254 & 1];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO atan
|
||||
// TODO bitshift
|
||||
// TODO ceiling
|
||||
// TODO copy
|
||||
// TODO cos
|
||||
it('converts to int', function() {
|
||||
var stack = evaluate('{ 9.9 cvi }');
|
||||
var expectedStack = [9];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('converts negatives to int', function() {
|
||||
var stack = evaluate('{ -9.9 cvi }');
|
||||
var expectedStack = [-9];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO cvr
|
||||
// TODO div
|
||||
it('duplicates', function() {
|
||||
var stack = evaluate('{ 99 dup }');
|
||||
var expectedStack = [99, 99];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO eq
|
||||
it('exchanges', function() {
|
||||
var stack = evaluate('{ 44 99 exch }');
|
||||
var expectedStack = [99, 44];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO exp
|
||||
// TODO false
|
||||
// TODO floor
|
||||
// TODO ge
|
||||
// TODO gt
|
||||
it('divides to integer', function() {
|
||||
var stack = evaluate('{ 2 3 idiv }');
|
||||
var expectedStack = [0];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('divides to negative integer', function() {
|
||||
var stack = evaluate('{ -2 3 idiv }');
|
||||
var expectedStack = [0];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('duplicates index', function() {
|
||||
var stack = evaluate('{ 4 3 2 1 2 index }');
|
||||
var expectedStack = [4, 3, 2, 1, 3];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO le
|
||||
// TODO ln
|
||||
// TODO log
|
||||
// TODO lt
|
||||
// TODO mod
|
||||
// TODO mul
|
||||
// TODO ne
|
||||
// TODO neg
|
||||
// TODO not
|
||||
// TODO or
|
||||
it('pops stack', function() {
|
||||
var stack = evaluate('{ 1 2 pop }');
|
||||
var expectedStack = [1];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('rolls stack right', function() {
|
||||
var stack = evaluate('{ 1 3 2 2 4 1 roll }');
|
||||
var expectedStack = [2, 1, 3, 2];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
it('rolls stack left', function() {
|
||||
var stack = evaluate('{ 1 3 2 2 4 -1 roll }');
|
||||
var expectedStack = [3, 2, 2, 1];
|
||||
expect(stack).toMatchArray(expectedStack);
|
||||
});
|
||||
// TODO round
|
||||
// TODO sin
|
||||
// TODO sqrt
|
||||
// TODO sub
|
||||
// TODO true
|
||||
// TODO truncate
|
||||
// TODO xor
|
||||
});
|
||||
});
|
||||
|
30
test/unit/jsTestDriver.conf
Normal file
30
test/unit/jsTestDriver.conf
Normal file
@ -0,0 +1,30 @@
|
||||
server: http://localhost:4224
|
||||
|
||||
load:
|
||||
- ../../external/jasmine/jasmine.js
|
||||
- ../../external/jasmineAdapter/JasmineAdapter.js
|
||||
- ../../src/obj.js
|
||||
- ../../src/core.js
|
||||
- ../../src/util.js
|
||||
- ../../src/canvas.js
|
||||
- ../../src/obj.js
|
||||
- ../../src/function.js
|
||||
- ../../src/charsets.js
|
||||
- ../../src/cidmaps.js
|
||||
- ../../src/colorspace.js
|
||||
- ../../src/crypto.js
|
||||
- ../../src/evaluator.js
|
||||
- ../../src/fonts.js
|
||||
- ../../src/glyphlist.js
|
||||
- ../../src/image.js
|
||||
- ../../src/metrics.js
|
||||
- ../../src/parser.js
|
||||
- ../../src/pattern.js
|
||||
- ../../src/stream.js
|
||||
- ../../src/worker.js
|
||||
- ../../external/jpgjs/jpg.js
|
||||
- ../unit/obj_spec.js
|
||||
- ../unit/function_spec.js
|
||||
- ../unit/crypto_spec.js
|
||||
- ../unit/stream_spec.js
|
||||
|
@ -3,14 +3,147 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
describe("obj", function() {
|
||||
describe('obj', function() {
|
||||
|
||||
describe("Name", function() {
|
||||
it("should retain the given name", function() {
|
||||
var givenName = "Font";
|
||||
describe('Name', function() {
|
||||
it('should retain the given name', function() {
|
||||
var givenName = 'Font';
|
||||
var name = new Name(givenName);
|
||||
expect(name.name).toEqual(givenName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Cmd', function() {
|
||||
it('should retain the given cmd name', function() {
|
||||
var givenCmd = 'BT';
|
||||
var cmd = new Cmd(givenCmd);
|
||||
expect(cmd.cmd).toEqual(givenCmd);
|
||||
});
|
||||
|
||||
it('should create only one object for a command and cache it', function() {
|
||||
var firstBT = Cmd.get('BT');
|
||||
var secondBT = Cmd.get('BT');
|
||||
var firstET = Cmd.get('ET');
|
||||
var secondET = Cmd.get('ET');
|
||||
expect(firstBT).toBe(secondBT);
|
||||
expect(firstET).toBe(secondET);
|
||||
expect(firstBT).not.toBe(firstET);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dict', function() {
|
||||
var checkInvalidHasValues = function(dict) {
|
||||
expect(dict.has()).toBeFalsy();
|
||||
expect(dict.has('Prev')).toBeFalsy();
|
||||
};
|
||||
|
||||
var checkInvalidKeyValues = function(dict) {
|
||||
expect(dict.get()).toBeUndefined();
|
||||
expect(dict.get('Prev')).toBeUndefined();
|
||||
expect(dict.get('Decode', 'D')).toBeUndefined();
|
||||
|
||||
// Note that the getter with three arguments breaks the pattern here.
|
||||
expect(dict.get('FontFile', 'FontFile2', 'FontFile3')).toBeNull();
|
||||
};
|
||||
|
||||
var emptyDict, dictWithSizeKey, dictWithManyKeys;
|
||||
var storedSize = 42;
|
||||
var testFontFile = 'file1';
|
||||
var testFontFile2 = 'file2';
|
||||
var testFontFile3 = 'file3';
|
||||
|
||||
beforeEach(function() {
|
||||
emptyDict = new Dict();
|
||||
|
||||
dictWithSizeKey = new Dict();
|
||||
dictWithSizeKey.set('Size', storedSize);
|
||||
|
||||
dictWithManyKeys = new Dict();
|
||||
dictWithManyKeys.set('FontFile', testFontFile);
|
||||
dictWithManyKeys.set('FontFile2', testFontFile2);
|
||||
dictWithManyKeys.set('FontFile3', testFontFile3);
|
||||
});
|
||||
|
||||
it('should return invalid values for unknown keys', function() {
|
||||
checkInvalidHasValues(emptyDict);
|
||||
checkInvalidKeyValues(emptyDict);
|
||||
});
|
||||
|
||||
it('should return correct value for stored Size key', function() {
|
||||
expect(dictWithSizeKey.has('Size')).toBeTruthy();
|
||||
|
||||
expect(dictWithSizeKey.get('Size')).toEqual(storedSize);
|
||||
expect(dictWithSizeKey.get('Prev', 'Size')).toEqual(storedSize);
|
||||
expect(dictWithSizeKey.get('Prev', 'Root', 'Size')).toEqual(storedSize);
|
||||
});
|
||||
|
||||
it('should return invalid values for unknown keys when Size key is stored',
|
||||
function() {
|
||||
checkInvalidHasValues(dictWithSizeKey);
|
||||
checkInvalidKeyValues(dictWithSizeKey);
|
||||
});
|
||||
|
||||
it('should return correct value for stored Size key with undefined value',
|
||||
function() {
|
||||
var dict = new Dict();
|
||||
dict.set('Size');
|
||||
|
||||
expect(dict.has('Size')).toBeTruthy();
|
||||
|
||||
checkInvalidKeyValues(dict);
|
||||
});
|
||||
|
||||
it('should return correct values for multiple stored keys', function() {
|
||||
expect(dictWithManyKeys.has('FontFile')).toBeTruthy();
|
||||
expect(dictWithManyKeys.has('FontFile2')).toBeTruthy();
|
||||
expect(dictWithManyKeys.has('FontFile3')).toBeTruthy();
|
||||
|
||||
expect(dictWithManyKeys.get('FontFile3')).toEqual(testFontFile3);
|
||||
expect(dictWithManyKeys.get('FontFile2', 'FontFile3'))
|
||||
.toEqual(testFontFile2);
|
||||
expect(dictWithManyKeys.get('FontFile', 'FontFile2', 'FontFile3'))
|
||||
.toEqual(testFontFile);
|
||||
});
|
||||
|
||||
it('should callback for each stored key', function() {
|
||||
var callbackSpy = jasmine.createSpy('spy on callback in dictionary');
|
||||
|
||||
dictWithManyKeys.forEach(callbackSpy);
|
||||
|
||||
expect(callbackSpy).wasCalled();
|
||||
expect(callbackSpy.argsForCall[0]).toEqual(['FontFile', testFontFile]);
|
||||
expect(callbackSpy.argsForCall[1]).toEqual(['FontFile2', testFontFile2]);
|
||||
expect(callbackSpy.argsForCall[2]).toEqual(['FontFile3', testFontFile3]);
|
||||
expect(callbackSpy.callCount).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ref', function() {
|
||||
it('should retain the stored values', function() {
|
||||
var storedNum = 4;
|
||||
var storedGen = 2;
|
||||
var ref = new Ref(storedNum, storedGen);
|
||||
expect(ref.num).toEqual(storedNum);
|
||||
expect(ref.gen).toEqual(storedGen);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RefSet', function() {
|
||||
it('should have a stored value', function() {
|
||||
var ref = new Ref(4, 2);
|
||||
var refset = new RefSet();
|
||||
refset.put(ref);
|
||||
expect(refset.has(ref)).toBeTruthy();
|
||||
});
|
||||
it('should not have an unknown value', function() {
|
||||
var ref = new Ref(4, 2);
|
||||
var refset = new RefSet();
|
||||
expect(refset.has(ref)).toBeFalsy();
|
||||
|
||||
refset.put(ref);
|
||||
var anotherRef = new Ref(2, 4);
|
||||
expect(refset.has(anotherRef)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
25
test/unit/stream_spec.js
Normal file
25
test/unit/stream_spec.js
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('stream', function() {
|
||||
|
||||
describe('PredictorStream', function() {
|
||||
it('should decode simple predictor data', function() {
|
||||
var dict = new Dict();
|
||||
dict.set('Predictor', 12);
|
||||
dict.set('Colors', 1);
|
||||
dict.set('BitsPerComponent', 8);
|
||||
dict.set('Columns', 2);
|
||||
|
||||
var input = new Stream(new Uint8Array([2, 100, 3, 2, 1, 255, 2, 1, 255]),
|
||||
0, 9, dict);
|
||||
var predictor = new PredictorStream(input, dict);
|
||||
var result = predictor.getBytes(6);
|
||||
|
||||
expect(result).toEqual(new Uint8Array([100, 3, 101, 2, 102, 1]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
3
test/unit/test_reports/.gitignore
vendored
Normal file
3
test/unit/test_reports/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
TEST*
|
||||
temp*
|
||||
|
@ -1,51 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>pdf.js unit test</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="../../external/jasmine/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="../../external/jasmine/jasmine.css">
|
||||
|
||||
<script type="text/javascript" src="../../external/jasmine/jasmine.js"></script>
|
||||
<script type="text/javascript" src="../../external/jasmine/jasmine-html.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script type="text/javascript" src="obj_spec.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
<script type="text/javascript" src="../../src/obj.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
'use strict';
|
||||
|
||||
(function pdfJsUnitTest() {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var trivialReporter = new jasmine.TrivialReporter();
|
||||
|
||||
jasmineEnv.addReporter(trivialReporter);
|
||||
|
||||
jasmineEnv.specFilter = function pdfJsUnitTestSpecFilter(spec) {
|
||||
return trivialReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function pdfJsUnitTestOnload() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
execJasmine();
|
||||
};
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -5,11 +5,16 @@
|
||||
|
||||
// Checking if the typed arrays are supported
|
||||
(function checkTypedArrayCompatibility() {
|
||||
if (typeof Uint8Array !== 'undefined')
|
||||
if (typeof Uint8Array !== 'undefined') {
|
||||
// some mobile version might not support Float64Array
|
||||
if (typeof Float64Array === 'undefined')
|
||||
window.Float64Array = Float32Array;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function subarray(start, end) {
|
||||
return this.slice(start, end);
|
||||
return new TypedArray(this.slice(start, end));
|
||||
}
|
||||
|
||||
function setArrayOffset(array, offset) {
|
||||
@ -46,6 +51,8 @@
|
||||
window.Uint32Array = TypedArray;
|
||||
window.Int32Array = TypedArray;
|
||||
window.Uint16Array = TypedArray;
|
||||
window.Float32Array = TypedArray;
|
||||
window.Float64Array = TypedArray;
|
||||
})();
|
||||
|
||||
// Object.create() ?
|
||||
|
3
web/images/check.svg
Normal file
3
web/images/check.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg height="40" width="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.379,14.729 5.208,11.899 12.958,19.648 25.877,6.733 28.707,9.561 12.958,25.308z" fill="#333333"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 188 B |
3
web/images/comment.svg
Normal file
3
web/images/comment.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg height="40" width="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z" fill="#333333"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 289 B |
@ -15,6 +15,7 @@ body {
|
||||
/* === Toolbar === */
|
||||
#controls {
|
||||
background-color: #eee;
|
||||
background: -o-linear-gradient(bottom,#eee 0%,#fff 100%);
|
||||
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
||||
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
|
||||
border-bottom: 1px solid #666;
|
||||
@ -82,6 +83,7 @@ span#info {
|
||||
bottom: 18px;
|
||||
left: -290px;
|
||||
transition: left 0.25s ease-in-out 1s;
|
||||
-o-transition: left 0.25s ease-in-out 1s;
|
||||
-moz-transition: left 0.25s ease-in-out 1s;
|
||||
-webkit-transition: left 0.25s ease-in-out 1s;
|
||||
z-index: 1;
|
||||
@ -90,6 +92,7 @@ span#info {
|
||||
#sidebar:hover {
|
||||
left: 0px;
|
||||
transition: left 0.25s ease-in-out 0s;
|
||||
-o-transition: left 0.25s ease-in-out 0s;
|
||||
-moz-transition: left 0.25s ease-in-out 0s;
|
||||
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||
}
|
||||
@ -247,6 +250,35 @@ canvas {
|
||||
line-height:1.3;
|
||||
}
|
||||
|
||||
.annotComment > div {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.annotComment > img {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.annotComment > img:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.annotComment > div {
|
||||
padding: 0.2em;
|
||||
max-width: 20em;
|
||||
background-color: #F1E47B;
|
||||
box-shadow: 0px 2px 10px #333;
|
||||
-moz-box-shadow: 0px 2px 10px #333;
|
||||
-webkit-box-shadow: 0px 2px 10px #333;
|
||||
}
|
||||
|
||||
.annotComment > div > h1 {
|
||||
font-weight: normal;
|
||||
font-size: 1.2em;
|
||||
border-bottom: 1px solid #000000;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* TODO: file FF bug to support ::-moz-selection:window-inactive
|
||||
so we can override the opaque grey background when the window is inactive;
|
||||
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
|
||||
@ -298,7 +330,7 @@ canvas {
|
||||
color: black;
|
||||
padding: 3px;
|
||||
margin: 3px;
|
||||
white-space: pre;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
.clearBoth {
|
||||
|
@ -67,10 +67,11 @@
|
||||
<option value="0.75">75%</option>
|
||||
<option value="1">100%</option>
|
||||
<option value="1.25">125%</option>
|
||||
<option value="1.5" selected="selected">150%</option>
|
||||
<option value="1.5">150%</option>
|
||||
<option value="2">200%</option>
|
||||
<option id="pageWidthOption" value="page-width">Page Width</option>
|
||||
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
||||
<option id="pageAutoOption" value="auto" selected="selected">Auto</option>
|
||||
</select>
|
||||
|
||||
<div class="separator"></div>
|
||||
@ -113,7 +114,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearBoth"></div>
|
||||
<div id="errorMoreInfo" hidden='true'></div>
|
||||
<textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
|
396
web/viewer.js
396
web/viewer.js
@ -4,14 +4,15 @@
|
||||
'use strict';
|
||||
|
||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||
var kDefaultScale = 1.5;
|
||||
var kDefaultScale = 'auto';
|
||||
var kDefaultScaleDelta = 1.1;
|
||||
var kCacheSize = 20;
|
||||
var kCssUnits = 96.0 / 72.0;
|
||||
var kScrollbarPadding = 40;
|
||||
var kMinScale = 0.25;
|
||||
var kMaxScale = 4.0;
|
||||
|
||||
var kImageDirectory = './images/';
|
||||
var kSettingsMemory = 20;
|
||||
|
||||
var Cache = function cacheCache(size) {
|
||||
var data = [];
|
||||
@ -25,16 +26,136 @@ var Cache = function cacheCache(size) {
|
||||
};
|
||||
};
|
||||
|
||||
var RenderingQueue = (function RenderingQueueClosure() {
|
||||
function RenderingQueue() {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
RenderingQueue.prototype = {
|
||||
enqueueDraw: function RenderingQueueEnqueueDraw(item) {
|
||||
if (!item.drawingRequired())
|
||||
return; // as no redraw required, no need for queueing.
|
||||
|
||||
if ('rendering' in item)
|
||||
return; // is already in the queue
|
||||
|
||||
item.rendering = true;
|
||||
this.items.push(item);
|
||||
if (this.items.length > 1)
|
||||
return; // not first item
|
||||
|
||||
item.draw(this.continueExecution.bind(this));
|
||||
},
|
||||
continueExecution: function RenderingQueueContinueExecution() {
|
||||
var item = this.items.shift();
|
||||
delete item.rendering;
|
||||
|
||||
if (this.items.length == 0)
|
||||
return; // queue is empty
|
||||
|
||||
item = this.items[0];
|
||||
item.draw(this.continueExecution.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
return RenderingQueue;
|
||||
})();
|
||||
|
||||
// Settings Manager - This is a utility for saving settings
|
||||
// First we see if localStorage is available, FF bug #495747
|
||||
// If not, we use FUEL in FF
|
||||
var Settings = (function SettingsClosure() {
|
||||
var isLocalStorageEnabled = (function localStorageEnabledTest() {
|
||||
try {
|
||||
localStorage;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
var extPrefix = 'extensions.uriloader@pdf.js';
|
||||
var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled;
|
||||
var inPrivateBrowsing = false;
|
||||
if (isExtension) {
|
||||
var pbs = Components.classes['@mozilla.org/privatebrowsing;1']
|
||||
.getService(Components.interfaces.nsIPrivateBrowsingService);
|
||||
inPrivateBrowsing = pbs.privateBrowsingEnabled;
|
||||
}
|
||||
|
||||
function Settings(fingerprint) {
|
||||
var database = null;
|
||||
var index;
|
||||
if (inPrivateBrowsing)
|
||||
return false;
|
||||
else if (isExtension)
|
||||
database = Application.prefs.getValue(extPrefix + '.database', '{}');
|
||||
else if (isLocalStorageEnabled)
|
||||
database = localStorage.getItem('database') || '{}';
|
||||
else
|
||||
return false;
|
||||
|
||||
database = JSON.parse(database);
|
||||
if (!('files' in database))
|
||||
database.files = [];
|
||||
if (database.files.length >= kSettingsMemory)
|
||||
database.files.shift();
|
||||
for (var i = 0, length = database.files.length; i < length; i++) {
|
||||
var branch = database.files[i];
|
||||
if (branch.fingerprint == fingerprint) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typeof index != 'number')
|
||||
index = database.files.push({fingerprint: fingerprint}) - 1;
|
||||
this.file = database.files[index];
|
||||
this.database = database;
|
||||
if (isExtension)
|
||||
Application.prefs.setValue(extPrefix + '.database',
|
||||
JSON.stringify(database));
|
||||
else if (isLocalStorageEnabled)
|
||||
localStorage.setItem('database', JSON.stringify(database));
|
||||
}
|
||||
|
||||
Settings.prototype = {
|
||||
set: function settingsSet(name, val) {
|
||||
if (inPrivateBrowsing)
|
||||
return false;
|
||||
var file = this.file;
|
||||
file[name] = val;
|
||||
if (isExtension)
|
||||
Application.prefs.setValue(extPrefix + '.database',
|
||||
JSON.stringify(this.database));
|
||||
else if (isLocalStorageEnabled)
|
||||
localStorage.setItem('database', JSON.stringify(this.database));
|
||||
},
|
||||
|
||||
get: function settingsGet(name, defaultValue) {
|
||||
if (inPrivateBrowsing)
|
||||
return defaultValue;
|
||||
else
|
||||
return this.file[name] || defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
return Settings;
|
||||
})();
|
||||
|
||||
var cache = new Cache(kCacheSize);
|
||||
var renderingQueue = new RenderingQueue();
|
||||
var currentPageNumber = 1;
|
||||
|
||||
var PDFView = {
|
||||
pages: [],
|
||||
thumbnails: [],
|
||||
currentScale: kDefaultScale,
|
||||
currentScale: 0,
|
||||
currentScaleValue: null,
|
||||
initialBookmark: document.location.hash.substring(1),
|
||||
|
||||
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
||||
if (val == this.currentScale)
|
||||
return;
|
||||
|
||||
var pages = this.pages;
|
||||
for (var i = 0; i < pages.length; i++)
|
||||
pages[i].update(val * kCssUnits);
|
||||
@ -55,6 +176,7 @@ var PDFView = {
|
||||
return;
|
||||
|
||||
var scale = parseFloat(value);
|
||||
this.currentScaleValue = value;
|
||||
if (scale) {
|
||||
this.setScale(scale, true);
|
||||
return;
|
||||
@ -73,6 +195,10 @@ var PDFView = {
|
||||
this.setScale(
|
||||
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
||||
}
|
||||
if ('auto' == value)
|
||||
this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings);
|
||||
|
||||
selectScaleOption(value);
|
||||
},
|
||||
|
||||
zoomIn: function pdfViewZoomIn() {
|
||||
@ -223,13 +349,14 @@ var PDFView = {
|
||||
};
|
||||
moreInfoButton.removeAttribute('hidden');
|
||||
lessInfoButton.setAttribute('hidden', 'true');
|
||||
errorMoreInfo.innerHTML = 'PDF.JS Build: ' + PDFJS.build + '\n';
|
||||
errorMoreInfo.value = 'PDF.JS Build: ' + PDFJS.build + '\n';
|
||||
|
||||
if (moreInfo) {
|
||||
errorMoreInfo.innerHTML += 'Message: ' + moreInfo.message;
|
||||
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
||||
if (moreInfo.stack)
|
||||
errorMoreInfo.innerHTML += '\n' + 'Stack: ' + moreInfo.stack;
|
||||
errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack;
|
||||
}
|
||||
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
|
||||
},
|
||||
|
||||
progress: function pdfViewProgress(level) {
|
||||
@ -243,6 +370,7 @@ var PDFView = {
|
||||
// when page is painted, using the image as thumbnail base
|
||||
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
||||
thumbnailView.setImage(pageView.canvas);
|
||||
preDraw();
|
||||
};
|
||||
}
|
||||
|
||||
@ -272,8 +400,20 @@ var PDFView = {
|
||||
this.error('An error occurred while reading the PDF.', e);
|
||||
}
|
||||
var pagesCount = pdf.numPages;
|
||||
var id = pdf.fingerprint;
|
||||
var storedHash = null;
|
||||
document.getElementById('numPages').innerHTML = pagesCount;
|
||||
document.getElementById('pageNumber').max = pagesCount;
|
||||
PDFView.documentFingerprint = id;
|
||||
var store = PDFView.store = new Settings(id);
|
||||
if (store.get('exists', false)) {
|
||||
var page = store.get('page', '1');
|
||||
var zoom = store.get('zoom', PDFView.currentScale);
|
||||
var left = store.get('scrollLeft', '0');
|
||||
var top = store.get('scrollTop', '0');
|
||||
|
||||
storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
|
||||
}
|
||||
|
||||
var pages = this.pages = [];
|
||||
var pagesRefMap = {};
|
||||
@ -294,7 +434,6 @@ var PDFView = {
|
||||
|
||||
this.pagesRefMap = pagesRefMap;
|
||||
this.destinations = pdf.catalog.destinations;
|
||||
this.setScale(scale || kDefaultScale, true);
|
||||
|
||||
if (pdf.catalog.documentOutline) {
|
||||
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
||||
@ -303,12 +442,20 @@ var PDFView = {
|
||||
this.switchSidebarView('outline');
|
||||
}
|
||||
|
||||
// Reset the current scale, as otherwise the page's scale might not get
|
||||
// updated if the zoom level stayed the same.
|
||||
this.currentScale = 0;
|
||||
this.currentScaleValue = null;
|
||||
if (this.initialBookmark) {
|
||||
this.setHash(this.initialBookmark);
|
||||
this.initialBookmark = null;
|
||||
}
|
||||
else
|
||||
else if (storedHash)
|
||||
this.setHash(storedHash);
|
||||
else {
|
||||
this.parseScale(scale || kDefaultScale, true);
|
||||
this.page = 1;
|
||||
}
|
||||
},
|
||||
|
||||
setHash: function pdfViewSetHash(hash) {
|
||||
@ -334,8 +481,16 @@ var PDFView = {
|
||||
if ('zoom' in params) {
|
||||
var zoomArgs = params.zoom.split(','); // scale,left,top
|
||||
// building destination array
|
||||
var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0),
|
||||
(zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100];
|
||||
|
||||
// If the zoom value, it has to get divided by 100. If it is a string,
|
||||
// it should stay as it is.
|
||||
var zoomArg = zoomArgs[0];
|
||||
var zoomArgNumber = parseFloat(zoomArg);
|
||||
if (zoomArgNumber)
|
||||
zoomArg = zoomArgNumber / 100;
|
||||
|
||||
var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0),
|
||||
(zoomArgs[2] | 0), zoomArg];
|
||||
var currentPage = this.pages[pageNumber - 1];
|
||||
currentPage.scrollIntoView(dest);
|
||||
} else
|
||||
@ -475,6 +630,41 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
element.style.height = Math.ceil(item.height * scale) + 'px';
|
||||
return element;
|
||||
}
|
||||
function createCommentAnnotation(type, item) {
|
||||
var container = document.createElement('section');
|
||||
container.className = 'annotComment';
|
||||
|
||||
var image = createElementWithStyle('img', item);
|
||||
image.src = kImageDirectory + type.toLowerCase() + '.svg';
|
||||
var content = document.createElement('div');
|
||||
content.setAttribute('hidden', true);
|
||||
var title = document.createElement('h1');
|
||||
var text = document.createElement('p');
|
||||
var offsetPos = Math.floor(item.x - view.x + item.width);
|
||||
content.style.left = (offsetPos * scale) + 'px';
|
||||
content.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
|
||||
title.textContent = item.title;
|
||||
|
||||
if (!item.content) {
|
||||
content.setAttribute('hidden', true);
|
||||
} else {
|
||||
text.innerHTML = item.content.replace('\n', '<br />');
|
||||
image.addEventListener('mouseover', function annotationImageOver() {
|
||||
this.nextSibling.removeAttribute('hidden');
|
||||
}, false);
|
||||
|
||||
image.addEventListener('mouseout', function annotationImageOut() {
|
||||
this.nextSibling.setAttribute('hidden', true);
|
||||
}, false);
|
||||
}
|
||||
|
||||
content.appendChild(title);
|
||||
content.appendChild(text);
|
||||
container.appendChild(image);
|
||||
container.appendChild(content);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
var items = content.getAnnotations();
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
@ -487,6 +677,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
bindLink(link, ('dest' in item) ? item.dest : null);
|
||||
div.appendChild(link);
|
||||
break;
|
||||
case 'Text':
|
||||
var comment = createCommentAnnotation(item.name, item);
|
||||
if (comment)
|
||||
div.appendChild(comment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -569,10 +764,15 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
}, 0);
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw() {
|
||||
if (div.hasChildNodes()) {
|
||||
this.drawingRequired = function() {
|
||||
return !div.hasChildNodes();
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw(callback) {
|
||||
if (!this.drawingRequired()) {
|
||||
this.updateStats();
|
||||
return false;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
@ -581,9 +781,13 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
div.appendChild(canvas);
|
||||
this.canvas = canvas;
|
||||
|
||||
var textLayer = document.createElement('div');
|
||||
textLayer.className = 'textLayer';
|
||||
div.appendChild(textLayer);
|
||||
var textLayerDiv = null;
|
||||
if (!PDFJS.disableTextLayer) {
|
||||
textLayerDiv = document.createElement('div');
|
||||
textLayerDiv.className = 'textLayer';
|
||||
div.appendChild(textLayerDiv);
|
||||
}
|
||||
var textLayer = textLayerDiv ? new TextLayerBuilder(textLayerDiv) : null;
|
||||
|
||||
var scale = this.scale;
|
||||
canvas.width = pageWidth * scale;
|
||||
@ -604,13 +808,14 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
this.updateStats();
|
||||
if (this.onAfterDraw)
|
||||
this.onAfterDraw();
|
||||
|
||||
cache.push(this);
|
||||
callback();
|
||||
}).bind(this), textLayer
|
||||
);
|
||||
|
||||
setupAnnotations(this.content, this.scale);
|
||||
div.setAttribute('data-loaded', true);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.updateStats = function pageViewUpdateStats() {
|
||||
@ -677,12 +882,20 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
return ctx;
|
||||
}
|
||||
|
||||
this.draw = function thumbnailViewDraw() {
|
||||
if (this.hasImage)
|
||||
this.drawingRequired = function thumbnailViewDrawingRequired() {
|
||||
return !this.hasImage;
|
||||
};
|
||||
|
||||
this.draw = function thumbnailViewDraw(callback) {
|
||||
if (this.hasImage) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = getPageDrawContext();
|
||||
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {});
|
||||
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {
|
||||
callback();
|
||||
});
|
||||
|
||||
this.hasImage = true;
|
||||
};
|
||||
@ -735,6 +948,53 @@ var DocumentOutlineView = function documentOutlineView(outline) {
|
||||
}
|
||||
};
|
||||
|
||||
var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
||||
this.textLayerDiv = textLayerDiv;
|
||||
|
||||
this.beginLayout = function textLayerBuilderBeginLayout() {
|
||||
this.textDivs = [];
|
||||
this.textLayerQueue = [];
|
||||
};
|
||||
|
||||
this.endLayout = function textLayerBuilderEndLayout() {
|
||||
var self = this;
|
||||
var textDivs = this.textDivs;
|
||||
var textLayerDiv = this.textLayerDiv;
|
||||
this.textLayerTimer = setInterval(function renderTextLayer() {
|
||||
if (textDivs.length === 0) {
|
||||
clearInterval(self.textLayerTimer);
|
||||
return;
|
||||
}
|
||||
var textDiv = textDivs.shift();
|
||||
if (textDiv.dataset.textLength >= 1) { // avoid div by zero
|
||||
textLayerDiv.appendChild(textDiv);
|
||||
// Adjust div width (via letterSpacing) to match canvas text
|
||||
// Due to the .offsetWidth calls, this is slow
|
||||
textDiv.style.letterSpacing =
|
||||
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
||||
(textDiv.dataset.textLength - 1)) + 'px';
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
|
||||
this.appendText = function textLayerBuilderAppendText(text,
|
||||
fontName, fontSize) {
|
||||
var textDiv = document.createElement('div');
|
||||
|
||||
// vScale and hScale already contain the scaling to pixel units
|
||||
var fontHeight = fontSize * text.geom.vScale;
|
||||
textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
|
||||
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = fontName || 'sans-serif';
|
||||
textDiv.style.left = text.geom.x + 'px';
|
||||
textDiv.style.top = (text.geom.y - fontHeight) + 'px';
|
||||
textDiv.textContent = text.str;
|
||||
textDiv.dataset.textLength = text.length;
|
||||
this.textDivs.push(textDiv);
|
||||
};
|
||||
};
|
||||
|
||||
window.addEventListener('load', function webViewerLoad(evt) {
|
||||
var params = document.location.search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
@ -742,7 +1002,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
||||
params[unescape(param[0])] = unescape(param[1]);
|
||||
}
|
||||
|
||||
var scale = ('scale' in params) ? params.scale : kDefaultScale;
|
||||
var scale = ('scale' in params) ? params.scale : 0;
|
||||
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
||||
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
||||
@ -751,7 +1011,10 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
||||
document.getElementById('fileInput').value = null;
|
||||
|
||||
if ('disableWorker' in params)
|
||||
PDFJS.disableWorker = params['disableWorker'] === 'true' ? true : false;
|
||||
PDFJS.disableWorker = (params['disableWorker'] === 'true');
|
||||
|
||||
if ('disableTextLayer' in params)
|
||||
PDFJS.disableTextLayer = (params['disableTextLayer'] === 'true');
|
||||
|
||||
var sidebarScrollView = document.getElementById('sidebarScrollView');
|
||||
sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true);
|
||||
@ -761,31 +1024,76 @@ window.addEventListener('unload', function webViewerUnload(evt) {
|
||||
window.scrollTo(0, 0);
|
||||
}, true);
|
||||
|
||||
/**
|
||||
* Render the next not yet visible page already such that it is
|
||||
* hopefully ready once the user scrolls to it.
|
||||
*/
|
||||
function preDraw() {
|
||||
var pages = PDFView.pages;
|
||||
var visible = PDFView.getVisiblePages();
|
||||
var last = visible[visible.length - 1];
|
||||
// PageView.id is the actual page number, which is + 1 compared
|
||||
// to the index in `pages`. That means, pages[last.id] is the next
|
||||
// PageView instance.
|
||||
if (pages[last.id] && pages[last.id].drawingRequired()) {
|
||||
renderingQueue.enqueueDraw(pages[last.id]);
|
||||
return;
|
||||
}
|
||||
// If there is nothing to draw on the next page, maybe the user
|
||||
// is scrolling up, so, let's try to render the next page *before*
|
||||
// the first visible page
|
||||
if (pages[visible[0].id - 2]) {
|
||||
renderingQueue.enqueueDraw(pages[visible[0].id - 2]);
|
||||
}
|
||||
}
|
||||
|
||||
function updateViewarea() {
|
||||
var visiblePages = PDFView.getVisiblePages();
|
||||
var pageToDraw;
|
||||
for (var i = 0; i < visiblePages.length; i++) {
|
||||
var page = visiblePages[i];
|
||||
if (PDFView.pages[page.id - 1].draw())
|
||||
cache.push(page.view);
|
||||
var pageObj = PDFView.pages[page.id - 1];
|
||||
|
||||
pageToDraw |= pageObj.drawingRequired();
|
||||
renderingQueue.enqueueDraw(pageObj);
|
||||
}
|
||||
|
||||
if (!visiblePages.length)
|
||||
return;
|
||||
|
||||
// If there is no need to draw a page that is currenlty visible, preDraw the
|
||||
// next page the user might scroll to.
|
||||
if (!pageToDraw) {
|
||||
preDraw();
|
||||
}
|
||||
|
||||
updateViewarea.inProgress = true; // used in "set page"
|
||||
var currentId = PDFView.page;
|
||||
var firstPage = visiblePages[0];
|
||||
PDFView.page = firstPage.id;
|
||||
updateViewarea.inProgress = false;
|
||||
|
||||
var currentScale = PDFView.currentScale;
|
||||
var currentScaleValue = PDFView.currentScaleValue;
|
||||
var normalizedScaleValue = currentScaleValue == currentScale ?
|
||||
currentScale * 100 : currentScaleValue;
|
||||
|
||||
var kViewerTopMargin = 52;
|
||||
var pageNumber = firstPage.id;
|
||||
var pdfOpenParams = '#page=' + pageNumber;
|
||||
pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
|
||||
pdfOpenParams += '&zoom=' + normalizedScaleValue;
|
||||
var currentPage = PDFView.pages[pageNumber - 1];
|
||||
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
||||
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
||||
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
|
||||
|
||||
var store = PDFView.store;
|
||||
store.set('exists', true);
|
||||
store.set('page', pageNumber);
|
||||
store.set('zoom', normalizedScaleValue);
|
||||
store.set('scrollLeft', Math.round(topLeft.x));
|
||||
store.set('scrollTop', Math.round(topLeft.y));
|
||||
|
||||
document.getElementById('viewBookmark').href = pdfOpenParams;
|
||||
}
|
||||
|
||||
@ -808,7 +1116,7 @@ function updateThumbViewArea() {
|
||||
var visibleThumbs = PDFView.getVisibleThumbs();
|
||||
for (var i = 0; i < visibleThumbs.length; i++) {
|
||||
var thumb = visibleThumbs[i];
|
||||
PDFView.thumbnails[thumb.id - 1].draw();
|
||||
renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]);
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
@ -818,7 +1126,8 @@ window.addEventListener('webkitTransitionEnd', updateThumbViewArea, true);
|
||||
|
||||
window.addEventListener('resize', function webViewerResize(evt) {
|
||||
if (document.getElementById('pageWidthOption').selected ||
|
||||
document.getElementById('pageFitOption').selected)
|
||||
document.getElementById('pageFitOption').selected ||
|
||||
document.getElementById('pageAutoOption').selected)
|
||||
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
||||
updateViewarea();
|
||||
});
|
||||
@ -848,7 +1157,6 @@ window.addEventListener('change', function webViewerChange(evt) {
|
||||
// implemented in Firefox.
|
||||
var file = files[0];
|
||||
fileReader.readAsBinaryString(file);
|
||||
|
||||
document.title = file.name;
|
||||
|
||||
// URL does not reflect proper document location - hiding some icons.
|
||||
@ -856,20 +1164,9 @@ window.addEventListener('change', function webViewerChange(evt) {
|
||||
document.getElementById('download').setAttribute('hidden', 'true');
|
||||
}, true);
|
||||
|
||||
window.addEventListener('scalechange', function scalechange(evt) {
|
||||
var customScaleOption = document.getElementById('customScaleOption');
|
||||
customScaleOption.selected = false;
|
||||
|
||||
if (!evt.resetAutoSettings &&
|
||||
(document.getElementById('pageWidthOption').selected ||
|
||||
document.getElementById('pageFitOption').selected)) {
|
||||
updateViewarea();
|
||||
return;
|
||||
}
|
||||
|
||||
function selectScaleOption(value) {
|
||||
var options = document.getElementById('scaleSelect').options;
|
||||
var predefinedValueFound = false;
|
||||
var value = '' + evt.scale;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
var option = options[i];
|
||||
if (option.value != value) {
|
||||
@ -879,7 +1176,22 @@ window.addEventListener('scalechange', function scalechange(evt) {
|
||||
option.selected = true;
|
||||
predefinedValueFound = true;
|
||||
}
|
||||
return predefinedValueFound;
|
||||
}
|
||||
|
||||
window.addEventListener('scalechange', function scalechange(evt) {
|
||||
var customScaleOption = document.getElementById('customScaleOption');
|
||||
customScaleOption.selected = false;
|
||||
|
||||
if (!evt.resetAutoSettings &&
|
||||
(document.getElementById('pageWidthOption').selected ||
|
||||
document.getElementById('pageFitOption').selected ||
|
||||
document.getElementById('pageAutoOption').selected)) {
|
||||
updateViewarea();
|
||||
return;
|
||||
}
|
||||
|
||||
var predefinedValueFound = selectScaleOption('' + evt.scale);
|
||||
if (!predefinedValueFound) {
|
||||
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
||||
customScaleOption.selected = true;
|
||||
@ -897,6 +1209,8 @@ window.addEventListener('pagechange', function pagechange(evt) {
|
||||
}, true);
|
||||
|
||||
window.addEventListener('keydown', function keydown(evt) {
|
||||
if (evt.ctrlKey || evt.altKey || evt.shiftKey || evt.metaKey)
|
||||
return;
|
||||
var curElement = document.activeElement;
|
||||
if (curElement && curElement.tagName == 'INPUT')
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user