1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 05:02:36 +01:00

Merge pull request #105 from raziraz/master

plans page
This commit is contained in:
Hillel Coren 2014-04-30 20:24:26 +03:00
commit 2bda5683cf
35 changed files with 19228 additions and 19 deletions

View File

@ -6,9 +6,10 @@
<!--
<link href="{{ asset('vendor/bootstrap/dist/css/bootstrap.min.css') }}" rel="stylesheet" type="text/css"/>
<link href="{{ asset('css/bootstrap.splash.css') }}" rel="stylesheet" type="text/css"/>
<link href="{{ asset('css/splash.css') }}" rel="stylesheet" type="text/css"/>
-->
<link href="{{ asset('css/splash.css') }}" rel="stylesheet" type="text/css"/>
<!--
<link href="{{ asset('images/apple-touch-icon-114x114-precomposed.png') }}" rel="apple-touch-icon-precomposed" sizes="114x114">
<link href="{{ asset('images/apple-touch-icon-72x72-precomposed.png') }}" rel="apple-touch-icon-precomposed" sizes="72x72">
<link href="{{ asset('images/apple-touch-icon-57x57-precomposed.png') }}" rel="apple-touch-icon-precomposed">

View File

@ -2,12 +2,6 @@
@section('content')
<style>
section.body {
background-color: #f8f8f8 !important;
}
</style>
<section class="hero background hero3" data-speed="2" data-type="background">
<div class="caption-side"></div>
<div class="container">
@ -21,28 +15,85 @@
</div>
</section>
<section class="center" style="background-color:#f8f8f8">
<section class="plans center">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
&nbsp;<p/>
<h2>Go Pro to Unlock Premium Invoice Ninja Features</h2>
<p>We believe that the free version of Invoice Ninja is a truly awesome product loaded
with the key features you need to bill your clients electronically. But for those who
crave still more Ninja awesomeness, we've unmasked the Invoice Ninja Pro plan, which
offers more versatility, power and customization options for just $50 per year. </p>
<br/>&nbsp;<br/>
</div>
</div>
</div>
</section>
<div style="background-color:#f8f8f8">
<center><img src="{{ asset('images/pro-plan-chart.png') }}"/></center>
</div>
<div class="container">
<div class="row">
<div class="plans-table col-md-9">
<div class="col-md-4 desc hide-mobile">
<div class="cell"></div>
<div class="cell">Number of clients per account</div>
<div class="cell">Remove "Created by Invoice Ninja"</div>
<div class="cell">Custom invoice fields</div>
<div class="cell">Unlimited client invoices</div>
<div class="cell">Add your company logo</div>
<div class="cell">Live .PDF invoice creation </div>
<div class="cell">4 beatiful invoice templates</div>
<div class="cell">Accept credit card payments</div>
<div class="cell">Multiple currency & language support</div>
<div class="cell">Open source platform</div>
<div class="cell">Self-hosting available</div>
<div class="cell">Customize payment terms</div>
<div class="cell">Pricing</div>
<section style="background-color:#f8f8f8;padding-top:80px;padding-bottom:80px;">
</div>
<div class="free col-md-4">
<div class="cell">Free</div>
<div class="cell"><div class="hide-desktop">Number of clients per account</div><span>1000</span></div>
<div class="cell"><div class="hide-desktop">Remove "Created by Invoice Ninja"</div><span class="glyphicon glyphicon-remove"></div>
<div class="cell"><div class="hide-desktop">Custom invoice fields</div><span class="glyphicon glyphicon-remove"></div>
<div class="cell"><div class="hide-desktop">Unlimited client invoices</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Add your company logo</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Live .PDF invoice creation</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">4 beatiful invoice templates</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Accept credit card payments</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Multiple currency & language support</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Open source platform</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Self-hosting available</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Customize payment terms</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell price"><div class="hide-desktop">Pricing</div><p>Free<span> /Always!</span></p></div>
</div>
<div class="pro col-md-4">
<div class="cell">Pro Plan<span class="glyphicon glyphicon-star"></div>
<div class="cell"><div class="hide-desktop">Number of clients per account</div><span style="color: #2299c0; font-size: 16px;">5000</span></div>
<div class="cell"><div class="hide-desktop">Remove "Created by Invoice Ninja"</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Custom invoice fields</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Unlimited client invoices</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Add your company logo</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Live .PDF invoice creation</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">4 beatiful invoice templates</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Accept credit card payments</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Multiple currency & language support</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Open source platform</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Self-hosting available</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell"><div class="hide-desktop">Customize payment terms</div><span class="glyphicon glyphicon-ok"></div>
<div class="cell price"><div class="hide-desktop">Pricing</div><p>$50<span> /Year</span></p></div>
<div class="cell">
<a href="#">
<div class="cta">
<h2 onclick="return getStarted()">GO PRO <span>+</span></h2>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<section class="upper-footer white-bg">
<div class="container">
<div class="row">
<div class="col-md-3 center-block">

View File

@ -282,11 +282,11 @@ section.blue .col-md-6 h1 span {
text-transform: lowercase;
}
section.about, section.team {
section.about, section.team, section.plans {
padding: 70px 0;
}
section.about h2, section.team h2 {
section.about h2, section.team h2, section.plans h2 {
font-size: 25px;
margin: 0 0 25px;
text-transform: none;
@ -749,3 +749,67 @@ div.fb_iframe_widget > span {
color: #fff;
background: #2e2b2b;
}
.plans-table {text-align:center; margin: 0 auto; float: none; margin-top: 60px; }
.plans-table .free, .plans-table .desc { padding: 0; }
.plans-table .free .cell { padding-right: 15px; }
.plans-table .desc .cell { text-align: right; padding-right: 15px; border-left: 1px solid #dfe0e1; font-size: 13px; font-weight: 800; }
.plans-table .pro .cell { border-left: 1px solid #cccccc; border-right: 1px solid #cccccc;}
.plans-table .cell {background-color: #fff; border-top: 1px solid #dfe0e1;padding: 18px 0; font-family: Roboto, sans-serif; height: 60px;}
.plans-table .cell:nth-child(odd){background-color: #fbfbfb;}
.plans-table .pro .cell:nth-child(odd){background-color: #f4f4f4;}
.plans-table .pro {
background-color: #2299c0;
overflow:hidden;
padding: 0;
-webkit-box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
-moz-box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
box-shadow: 0px 0px 15px 0px rgba(0, 5, 5, 0.2);
}
.plans-table .free .cell:first-child, .plans-table .pro .cell:first-child {color: #fff; text-transform: uppercase; font-size: 24px; font-weight:800; line-height: 60px; padding: 0; position: relative; bottom: -1px; border: none;}
.plans-table .free .cell:first-child {background-color: #9b9b9b; margin-right: 15px; padding-right: 0;}
.plans-table .free, .plans-table .desc {border-bottom: 1px solid #dfe0e1;}
.plans-table .pro .cell:first-child {background-color: #2299c0;}
.plans-table .pro .cell:last-child {padding: 0; border: none;}
.plans-table .desc .cell:first-child {background-color: transparent; border: none;}
.plans-table .glyphicon {color: #fff; border-radius: 50px; padding: 5px; font-size: 10px;}
.plans-table .glyphicon-remove {background-color: #da4830;}
.plans-table .glyphicon-ok {background-color: #35c156;}
.plans-table .glyphicon-star {border-radius: 0; background-color: #2e2b2b;
display: block;
width: 60px;
height: 30px;
position: absolute;
top: -5px;
right: -20px;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
padding: 13px 0 0 1px;
}
.plans-table .price {padding: 0; }
.plans-table .free .price p {color: #35c156;}
.plans-table .pro .price p {color: #2299c0;}
.plans-table .price p {font-size: 40px; text-transform: uppercase; font-weight: 800; margin: 0; line-height: 55px;}
.plans-table .price p span {font-size: 16px; text-transform: none; font-weight: 400;}
.plans-table a .cta h2 {background: #2299c0; color:#fff; margin: 0;}
.plans-table a .cta h2 span {background: #1e84a5;}
@media screen and (min-width: 769px) {
.hide-desktop {display: none;}
}
@media screen and (max-width: 769px) {
.hide-mobile {display: none;}
.plans-table .cell {height: auto; padding: 14px 0; }
.plans-table .free .cell { padding-right: 0; }
.plans-table .free .cell:first-child {margin-right: 0;}
.plans-table .cell div:first-child {margin-bottom: 5px;}
.plans-table .cell .cta {margin-bottom: 0 !important;}
.plans-table .pro {margin-top: 40px;}
}

Binary file not shown.

View File

@ -0,0 +1,135 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* jshint esnext:true */
/* globals Components, Services, XPCOMUtils, NetUtil, dump */
'use strict';
var EXPORTED_SYMBOLS = ['PdfRedirector'];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const PDF_CONTENT_TYPE = 'application/pdf';
const FIREFOX_ID = '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}';
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
function getDOMWindow(aChannel) {
var requestor = aChannel.notificationCallbacks ?
aChannel.notificationCallbacks :
aChannel.loadGroup.notificationCallbacks;
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
return win;
}
function getObjectUrl(window) {
// PlayPreview overlay "belongs" to the embed/object tag and consists of DIV
// and IFRAME. Starting from IFRAME and looking for first object tag.
var element = window.frameElement, containerElement;
if (!element) {
return null; // iframe tag
}
var tagName = element.nodeName;
while (tagName !== 'EMBED' && tagName !== 'OBJECT') {
containerElement = element;
element = element.parentNode;
if (!element) {
return null; // object tag was not found
}
tagName = element.nodeName;
}
// Checking if overlay is a proper PlayPreview overlay.
for (var i = 0; i < element.children.length; i++) {
if (element.children[i] === containerElement) {
return null; // invalid plugin element overlay
}
}
return element.srcURI.spec;
}
function PdfRedirector() {
}
PdfRedirector.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{8cbfd8d0-2042-4976-b3ef-d9dee1efb975}'),
classDescription: 'pdf.js Redirector',
contractID:
'@mozilla.org/streamconv;1?from=application/x-moz-playpreview-pdfjs&to=*/*',
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIStreamConverter,
Ci.nsIStreamListener,
Ci.nsIRequestObserver
]),
// nsIStreamConverter::convert
convert: function(aFromStream, aFromType, aToType, aCtxt) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
// nsIStreamConverter::asyncConvertData
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
// Store the listener passed to us
this.listener = aListener;
},
// nsIStreamListener::onDataAvailable
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
// Do nothing since all the data loading is handled by the viewer.
},
// nsIRequestObserver::onStartRequest
onStartRequest: function(aRequest, aContext) {
// Setup the request so we can use it below.
aRequest.QueryInterface(Ci.nsIChannel);
// Cancel the request so the viewer can handle it.
aRequest.cancel(Cr.NS_BINDING_ABORTED);
var domWindow = getDOMWindow(aRequest);
var pdfUrl = getObjectUrl(domWindow);
if (!pdfUrl) {
Services.console.logStringMessage(
'PdfRedirector.js: PDF location is not specified for OBJECT/EMBED tag');
return;
}
// Create a new channel that is viewer loaded as a resource.
var ioService = Services.io;
var channel = ioService.newChannel(pdfUrl, null, null);
channel.loadGroup = aRequest.loadGroup;
channel.asyncOpen(this.listener, aContext);
},
// nsIRequestObserver::onStopRequest
onStopRequest: function(aRequest, aContext, aStatusCode) {
// Do nothing
}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfRedirector]);

View File

@ -0,0 +1,963 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* jshint esnext:true */
/* globals Components, Services, XPCOMUtils, NetUtil, PrivateBrowsingUtils,
dump, NetworkManager, PdfJsTelemetry, DEFAULT_PREFERENCES */
'use strict';
var EXPORTED_SYMBOLS = ['PdfStreamConverter'];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
// True only if this is the version of pdf.js that is included with firefox.
const MOZ_CENTRAL = JSON.parse('PDFJSSCRIPT_MOZ_CENTRAL');
const PDFJS_EVENT_ID = 'pdf.js.message';
const PDF_CONTENT_TYPE = 'application/pdf';
const PREF_PREFIX = 'PDFJSSCRIPT_PREF_PREFIX';
const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html';
const MAX_DATABASE_LENGTH = 4096;
const MAX_STRING_PREF_LENGTH = 4096;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/NetUtil.jsm');
Cu.import('resource://pdf.js/network.js');
// Load the default preferences.
Cu.import('resource://pdf.js/default_preferences.js');
XPCOMUtils.defineLazyModuleGetter(this, 'PrivateBrowsingUtils',
'resource://gre/modules/PrivateBrowsingUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PdfJsTelemetry',
'resource://pdf.js/PdfJsTelemetry.jsm');
var Svc = {};
XPCOMUtils.defineLazyServiceGetter(Svc, 'mime',
'@mozilla.org/mime;1',
'nsIMIMEService');
function getChromeWindow(domWindow) {
var containingBrowser = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler;
return containingBrowser.ownerDocument.defaultView;
}
function setBoolPref(pref, value) {
Services.prefs.setBoolPref(pref, value);
}
function getBoolPref(pref, def) {
try {
return Services.prefs.getBoolPref(pref);
} catch (ex) {
return def;
}
}
function setIntPref(pref, value) {
Services.prefs.setIntPref(pref, value);
}
function getIntPref(pref, def) {
try {
return Services.prefs.getIntPref(pref);
} catch (ex) {
return def;
}
}
function setStringPref(pref, value) {
var str = Cc['@mozilla.org/supports-string;1']
.createInstance(Ci.nsISupportsString);
str.data = value;
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, str);
}
function getStringPref(pref, def) {
try {
return Services.prefs.getComplexValue(pref, Ci.nsISupportsString).data;
} catch (ex) {
return def;
}
}
function log(aMsg) {
if (!getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false))
return;
var msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
}
function getDOMWindow(aChannel) {
var requestor = aChannel.notificationCallbacks ?
aChannel.notificationCallbacks :
aChannel.loadGroup.notificationCallbacks;
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
return win;
}
function getLocalizedStrings(path) {
var stringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
getService(Ci.nsIStringBundleService).
createBundle('chrome://pdf.js/locale/' + path);
var map = {};
var enumerator = stringBundle.getSimpleEnumeration();
while (enumerator.hasMoreElements()) {
var string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
var key = string.key, property = 'textContent';
var i = key.lastIndexOf('.');
if (i >= 0) {
property = key.substring(i + 1);
key = key.substring(0, i);
}
if (!(key in map))
map[key] = {};
map[key][property] = string.value;
}
return map;
}
function getLocalizedString(strings, id, property) {
property = property || 'textContent';
if (id in strings)
return strings[id][property];
return id;
}
// PDF data storage
function PdfDataListener(length) {
this.length = length; // less than 0, if length is unknown
this.data = new Uint8Array(length >= 0 ? length : 0x10000);
this.loaded = 0;
}
PdfDataListener.prototype = {
append: function PdfDataListener_append(chunk) {
var willBeLoaded = this.loaded + chunk.length;
if (this.length >= 0 && this.length < willBeLoaded) {
this.length = -1; // reset the length, server is giving incorrect one
}
if (this.length < 0 && this.data.length < willBeLoaded) {
// data length is unknown and new chunk will not fit in the existing
// buffer, resizing the buffer by doubling the its last length
var newLength = this.data.length;
for (; newLength < willBeLoaded; newLength *= 2) {}
var newData = new Uint8Array(newLength);
newData.set(this.data);
this.data = newData;
}
this.data.set(chunk, this.loaded);
this.loaded = willBeLoaded;
this.onprogress(this.loaded, this.length >= 0 ? this.length : void(0));
},
getData: function PdfDataListener_getData() {
var data = this.data;
if (this.loaded != data.length)
data = data.subarray(0, this.loaded);
delete this.data; // releasing temporary storage
return data;
},
finish: function PdfDataListener_finish() {
this.isDataReady = true;
if (this.oncompleteCallback) {
this.oncompleteCallback(this.getData());
}
},
error: function PdfDataListener_error(errorCode) {
this.errorCode = errorCode;
if (this.oncompleteCallback) {
this.oncompleteCallback(null, errorCode);
}
},
onprogress: function() {},
get oncomplete() {
return this.oncompleteCallback;
},
set oncomplete(value) {
this.oncompleteCallback = value;
if (this.isDataReady) {
value(this.getData());
}
if (this.errorCode) {
value(null, this.errorCode);
}
}
};
// All the priviledged actions.
function ChromeActions(domWindow, contentDispositionFilename) {
this.domWindow = domWindow;
this.contentDispositionFilename = contentDispositionFilename;
this.telemetryState = {
documentInfo: false,
firstPageInfo: false,
streamTypesUsed: [],
startAt: Date.now()
};
}
ChromeActions.prototype = {
isInPrivateBrowsing: function() {
return PrivateBrowsingUtils.isWindowPrivate(this.domWindow);
},
download: function(data, sendResponse) {
var self = this;
var originalUrl = data.originalUrl;
// The data may not be downloaded so we need just retry getting the pdf with
// the original url.
var originalUri = NetUtil.newURI(data.originalUrl);
var filename = data.filename;
if (typeof filename !== 'string' || !/\.pdf$/i.test(filename)) {
filename = 'document.pdf';
}
var blobUri = data.blobUrl ? NetUtil.newURI(data.blobUrl) : originalUri;
var extHelperAppSvc =
Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
getService(Ci.nsIExternalHelperAppService);
var frontWindow = Cc['@mozilla.org/embedcomp/window-watcher;1'].
getService(Ci.nsIWindowWatcher).activeWindow;
var docIsPrivate = this.isInPrivateBrowsing();
var netChannel = NetUtil.newChannel(blobUri);
if ('nsIPrivateBrowsingChannel' in Ci &&
netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
netChannel.setPrivate(docIsPrivate);
}
NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
if (sendResponse)
sendResponse(true);
return;
}
// Create a nsIInputStreamChannel so we can set the url on the channel
// so the filename will be correct.
var channel = Cc['@mozilla.org/network/input-stream-channel;1'].
createInstance(Ci.nsIInputStreamChannel);
channel.QueryInterface(Ci.nsIChannel);
try {
// contentDisposition/contentDispositionFilename is readonly before FF18
channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT;
if (self.contentDispositionFilename) {
channel.contentDispositionFilename = self.contentDispositionFilename;
} else {
channel.contentDispositionFilename = filename;
}
} catch (e) {}
channel.setURI(originalUri);
channel.contentStream = aInputStream;
if ('nsIPrivateBrowsingChannel' in Ci &&
channel instanceof Ci.nsIPrivateBrowsingChannel) {
channel.setPrivate(docIsPrivate);
}
var listener = {
extListener: null,
onStartRequest: function(aRequest, aContext) {
this.extListener = extHelperAppSvc.doContent('application/pdf',
aRequest, frontWindow, false);
this.extListener.onStartRequest(aRequest, aContext);
},
onStopRequest: function(aRequest, aContext, aStatusCode) {
if (this.extListener)
this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
// Notify the content code we're done downloading.
if (sendResponse)
sendResponse(false);
},
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset,
aCount) {
this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
aOffset, aCount);
}
};
channel.asyncOpen(listener, null);
});
},
setDatabase: function(data) {
if (this.isInPrivateBrowsing())
return;
// Protect against something sending tons of data to setDatabase.
if (data.length > MAX_DATABASE_LENGTH)
return;
setStringPref(PREF_PREFIX + '.database', data);
},
getDatabase: function() {
if (this.isInPrivateBrowsing())
return '{}';
return getStringPref(PREF_PREFIX + '.database', '{}');
},
getLocale: function() {
return getStringPref('general.useragent.locale', 'en-US');
},
getStrings: function(data) {
try {
// Lazy initialization of localizedStrings
if (!('localizedStrings' in this))
this.localizedStrings = getLocalizedStrings('viewer.properties');
var result = this.localizedStrings[data];
return JSON.stringify(result || null);
} catch (e) {
log('Unable to retrive localized strings: ' + e);
return 'null';
}
},
pdfBugEnabled: function() {
return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false);
},
supportsIntegratedFind: function() {
// Integrated find is only supported when we're not in a frame and when the
// new find events code exists.
return this.domWindow.frameElement === null &&
getChromeWindow(this.domWindow).gFindBar &&
'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
},
supportsDocumentFonts: function() {
var prefBrowser = getIntPref('browser.display.use_document_fonts', 1);
var prefGfx = getBoolPref('gfx.downloadable_fonts.enabled', true);
return (!!prefBrowser && prefGfx);
},
supportsDocumentColors: function() {
return getBoolPref('browser.display.use_document_colors', true);
},
reportTelemetry: function (data) {
var probeInfo = JSON.parse(data);
switch (probeInfo.type) {
case 'documentInfo':
if (!this.telemetryState.documentInfo) {
PdfJsTelemetry.onDocumentVersion(probeInfo.version | 0);
PdfJsTelemetry.onDocumentGenerator(probeInfo.generator | 0);
if (probeInfo.formType) {
PdfJsTelemetry.onForm(probeInfo.formType === 'acroform');
}
this.telemetryState.documentInfo = true;
}
break;
case 'pageInfo':
if (!this.telemetryState.firstPageInfo) {
var duration = Date.now() - this.telemetryState.startAt;
PdfJsTelemetry.onTimeToView(duration);
this.telemetryState.firstPageInfo = true;
}
break;
case 'streamInfo':
if (!Array.isArray(probeInfo.streamTypes)) {
break;
}
for (var i = 0; i < probeInfo.streamTypes.length; i++) {
var streamTypeId = probeInfo.streamTypes[i] | 0;
if (streamTypeId >= 0 && streamTypeId < 10 &&
!this.telemetryState.streamTypesUsed[streamTypeId]) {
PdfJsTelemetry.onStreamType(streamTypeId);
this.telemetryState.streamTypesUsed[streamTypeId] = true;
}
}
break;
}
},
fallback: function(args, sendResponse) {
var featureId = args.featureId;
var url = args.url;
var self = this;
var domWindow = this.domWindow;
var strings = getLocalizedStrings('chrome.properties');
var message;
if (featureId === 'forms') {
message = getLocalizedString(strings, 'unsupported_feature_forms');
} else {
message = getLocalizedString(strings, 'unsupported_feature');
}
PdfJsTelemetry.onFallback();
var notificationBox = null;
try {
// Based on MDN's "Working with windows in chrome code"
var mainWindow = domWindow
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
var browser = mainWindow.gBrowser
.getBrowserForDocument(domWindow.top.document);
notificationBox = mainWindow.gBrowser.getNotificationBox(browser);
} catch (e) {
log('Unable to get a notification box for the fallback message');
return;
}
// Flag so we don't call the response callback twice, since if the user
// clicks open with different viewer both the button callback and
// eventCallback will be called.
var sentResponse = false;
var buttons = [{
label: getLocalizedString(strings, 'open_with_different_viewer'),
accessKey: getLocalizedString(strings, 'open_with_different_viewer',
'accessKey'),
callback: function() {
sentResponse = true;
sendResponse(true);
}
}];
notificationBox.appendNotification(message, 'pdfjs-fallback', null,
notificationBox.PRIORITY_INFO_LOW,
buttons,
function eventsCallback(eventType) {
// Currently there is only one event "removed" but if there are any other
// added in the future we still only care about removed at the moment.
if (eventType !== 'removed')
return;
// Don't send a response again if we already responded when the button was
// clicked.
if (!sentResponse)
sendResponse(false);
});
},
updateFindControlState: function(data) {
if (!this.supportsIntegratedFind())
return;
// Verify what we're sending to the findbar.
var result = data.result;
var findPrevious = data.findPrevious;
var findPreviousType = typeof findPrevious;
if ((typeof result !== 'number' || result < 0 || result > 3) ||
(findPreviousType !== 'undefined' && findPreviousType !== 'boolean')) {
return;
}
getChromeWindow(this.domWindow).gFindBar
.updateControlState(result, findPrevious);
},
setPreferences: function(prefs) {
var prefValue, defaultValue, prefName, prefType, defaultType;
for (var key in DEFAULT_PREFERENCES) {
prefValue = prefs[key];
defaultValue = DEFAULT_PREFERENCES[key];
prefName = (PREF_PREFIX + '.' + key);
if (prefValue === undefined || prefValue === defaultValue) {
Services.prefs.clearUserPref(prefName);
} else {
prefType = typeof prefValue;
defaultType = typeof defaultValue;
if (prefType !== defaultType) {
continue;
}
switch (defaultType) {
case 'boolean':
setBoolPref(prefName, prefValue);
break;
case 'number':
setIntPref(prefName, prefValue);
break;
case 'string':
// Protect against adding arbitrarily long strings in about:config.
if (prefValue.length <= MAX_STRING_PREF_LENGTH) {
setStringPref(prefName, prefValue);
}
break;
}
}
}
},
getPreferences: function() {
var currentPrefs = {};
var defaultValue, prefName;
for (var key in DEFAULT_PREFERENCES) {
defaultValue = DEFAULT_PREFERENCES[key];
prefName = (PREF_PREFIX + '.' + key);
switch (typeof defaultValue) {
case 'boolean':
currentPrefs[key] = getBoolPref(prefName, defaultValue);
break;
case 'number':
currentPrefs[key] = getIntPref(prefName, defaultValue);
break;
case 'string':
currentPrefs[key] = getStringPref(prefName, defaultValue);
break;
}
}
return JSON.stringify(currentPrefs);
}
};
var RangedChromeActions = (function RangedChromeActionsClosure() {
/**
* This is for range requests
*/
function RangedChromeActions(
domWindow, contentDispositionFilename, originalRequest,
dataListener) {
ChromeActions.call(this, domWindow, contentDispositionFilename);
this.dataListener = dataListener;
this.originalRequest = originalRequest;
this.pdfUrl = originalRequest.URI.spec;
this.contentLength = originalRequest.contentLength;
// Pass all the headers from the original request through
var httpHeaderVisitor = {
headers: {},
visitHeader: function(aHeader, aValue) {
if (aHeader === 'Range') {
// When loading the PDF from cache, firefox seems to set the Range
// request header to fetch only the unfetched portions of the file
// (e.g. 'Range: bytes=1024-'). However, we want to set this header
// manually to fetch the PDF in chunks.
return;
}
this.headers[aHeader] = aValue;
}
};
originalRequest.visitRequestHeaders(httpHeaderVisitor);
var self = this;
var xhr_onreadystatechange = function xhr_onreadystatechange() {
if (this.readyState === 1) { // LOADING
var netChannel = this.channel;
if ('nsIPrivateBrowsingChannel' in Ci &&
netChannel instanceof Ci.nsIPrivateBrowsingChannel) {
var docIsPrivate = self.isInPrivateBrowsing();
netChannel.setPrivate(docIsPrivate);
}
}
};
var getXhr = function getXhr() {
const XMLHttpRequest = Components.Constructor(
'@mozilla.org/xmlextras/xmlhttprequest;1');
var xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', xhr_onreadystatechange);
return xhr;
};
this.networkManager = new NetworkManager(this.pdfUrl, {
httpHeaders: httpHeaderVisitor.headers,
getXhr: getXhr
});
// If we are in range request mode, this means we manually issued xhr
// requests, which we need to abort when we leave the page
domWindow.addEventListener('unload', function unload(e) {
self.networkManager.abortAllRequests();
domWindow.removeEventListener(e.type, unload);
});
}
RangedChromeActions.prototype = Object.create(ChromeActions.prototype);
var proto = RangedChromeActions.prototype;
proto.constructor = RangedChromeActions;
proto.initPassiveLoading = function RangedChromeActions_initPassiveLoading() {
this.originalRequest.cancel(Cr.NS_BINDING_ABORTED);
this.originalRequest = null;
this.domWindow.postMessage({
pdfjsLoadAction: 'supportsRangedLoading',
pdfUrl: this.pdfUrl,
length: this.contentLength,
data: this.dataListener.getData()
}, '*');
this.dataListener = null;
return true;
};
proto.requestDataRange = function RangedChromeActions_requestDataRange(args) {
var begin = args.begin;
var end = args.end;
var domWindow = this.domWindow;
// TODO(mack): Support error handler. We're not currently not handling
// errors from chrome code for non-range requests, so this doesn't
// seem high-pri
this.networkManager.requestRange(begin, end, {
onDone: function RangedChromeActions_onDone(args) {
domWindow.postMessage({
pdfjsLoadAction: 'range',
begin: args.begin,
chunk: args.chunk
}, '*');
},
onProgress: function RangedChromeActions_onProgress(evt) {
domWindow.postMessage({
pdfjsLoadAction: 'rangeProgress',
loaded: evt.loaded,
}, '*');
}
});
};
return RangedChromeActions;
})();
var StandardChromeActions = (function StandardChromeActionsClosure() {
/**
* This is for a single network stream
*/
function StandardChromeActions(domWindow, contentDispositionFilename,
dataListener) {
ChromeActions.call(this, domWindow, contentDispositionFilename);
this.dataListener = dataListener;
}
StandardChromeActions.prototype = Object.create(ChromeActions.prototype);
var proto = StandardChromeActions.prototype;
proto.constructor = StandardChromeActions;
proto.initPassiveLoading =
function StandardChromeActions_initPassiveLoading() {
if (!this.dataListener) {
return false;
}
var self = this;
this.dataListener.onprogress = function ChromeActions_dataListenerProgress(
loaded, total) {
self.domWindow.postMessage({
pdfjsLoadAction: 'progress',
loaded: loaded,
total: total
}, '*');
};
this.dataListener.oncomplete = function ChromeActions_dataListenerComplete(
data, errorCode) {
self.domWindow.postMessage({
pdfjsLoadAction: 'complete',
data: data,
errorCode: errorCode
}, '*');
delete self.dataListener;
};
return true;
};
return StandardChromeActions;
})();
// Event listener to trigger chrome privedged code.
function RequestListener(actions) {
this.actions = actions;
}
// Receive an event and synchronously or asynchronously responds.
RequestListener.prototype.receive = function(event) {
var message = event.target;
var doc = message.ownerDocument;
var action = event.detail.action;
var data = event.detail.data;
var sync = event.detail.sync;
var actions = this.actions;
if (!(action in actions)) {
log('Unknown action: ' + action);
return;
}
if (sync) {
var response = actions[action].call(this.actions, data);
var detail = event.detail;
detail.__exposedProps__ = {response: 'r'};
detail.response = response;
} else {
var response;
if (!event.detail.callback) {
doc.documentElement.removeChild(message);
response = null;
} else {
response = function sendResponse(response) {
try {
var listener = doc.createEvent('CustomEvent');
listener.initCustomEvent('pdf.js.response', true, false,
{response: response,
__exposedProps__: {response: 'r'}});
return message.dispatchEvent(listener);
} catch (e) {
// doc is no longer accessible because the requestor is already
// gone. unloaded content cannot receive the response anyway.
return false;
}
};
}
actions[action].call(this.actions, data, response);
}
};
// Forwards events from the eventElement to the contentWindow only if the
// content window matches the currently selected browser window.
function FindEventManager(eventElement, contentWindow, chromeWindow) {
this.types = ['find',
'findagain',
'findhighlightallchange',
'findcasesensitivitychange'];
this.chromeWindow = chromeWindow;
this.contentWindow = contentWindow;
this.eventElement = eventElement;
}
FindEventManager.prototype.bind = function() {
var unload = function(e) {
this.unbind();
this.contentWindow.removeEventListener(e.type, unload);
}.bind(this);
this.contentWindow.addEventListener('unload', unload);
for (var i = 0; i < this.types.length; i++) {
var type = this.types[i];
this.eventElement.addEventListener(type, this, true);
}
};
FindEventManager.prototype.handleEvent = function(e) {
var chromeWindow = this.chromeWindow;
var contentWindow = this.contentWindow;
// Only forward the events if they are for our dom window.
if (chromeWindow.gBrowser.selectedBrowser.contentWindow === contentWindow) {
var detail = e.detail;
detail.__exposedProps__ = {
query: 'r',
caseSensitive: 'r',
highlightAll: 'r',
findPrevious: 'r'
};
var forward = contentWindow.document.createEvent('CustomEvent');
forward.initCustomEvent(e.type, true, true, detail);
contentWindow.dispatchEvent(forward);
e.preventDefault();
}
};
FindEventManager.prototype.unbind = function() {
for (var i = 0; i < this.types.length; i++) {
var type = this.types[i];
this.eventElement.removeEventListener(type, this, true);
}
};
function PdfStreamConverter() {
}
PdfStreamConverter.prototype = {
// properties required for XPCOM registration:
classID: Components.ID('{PDFJSSCRIPT_STREAM_CONVERTER_ID}'),
classDescription: 'pdf.js Component',
contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*',
QueryInterface: XPCOMUtils.generateQI([
Ci.nsISupports,
Ci.nsIStreamConverter,
Ci.nsIStreamListener,
Ci.nsIRequestObserver
]),
/*
* This component works as such:
* 1. asyncConvertData stores the listener
* 2. onStartRequest creates a new channel, streams the viewer
* 3. If range requests are supported:
* 3.1. Leave the request open until the viewer is ready to switch to
* range requests.
*
* If range rquests are not supported:
* 3.1. Read the stream as it's loaded in onDataAvailable to send
* to the viewer
*
* The convert function just returns the stream, it's just the synchronous
* version of asyncConvertData.
*/
// nsIStreamConverter::convert
convert: function(aFromStream, aFromType, aToType, aCtxt) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
// nsIStreamConverter::asyncConvertData
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
// Store the listener passed to us
this.listener = aListener;
},
// nsIStreamListener::onDataAvailable
onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
if (!this.dataListener) {
return;
}
var binaryStream = this.binaryStream;
binaryStream.setInputStream(aInputStream);
var chunk = binaryStream.readByteArray(aCount);
this.dataListener.append(chunk);
},
// nsIRequestObserver::onStartRequest
onStartRequest: function(aRequest, aContext) {
// Setup the request so we can use it below.
var isHttpRequest = false;
try {
aRequest.QueryInterface(Ci.nsIHttpChannel);
isHttpRequest = true;
} catch (e) {}
var rangeRequest = false;
if (isHttpRequest) {
var contentEncoding = 'identity';
try {
contentEncoding = aRequest.getResponseHeader('Content-Encoding');
} catch (e) {}
var acceptRanges;
try {
acceptRanges = aRequest.getResponseHeader('Accept-Ranges');
} catch (e) {}
var hash = aRequest.URI.ref;
rangeRequest = contentEncoding === 'identity' &&
acceptRanges === 'bytes' &&
aRequest.contentLength >= 0 &&
hash.indexOf('disableRange=true') < 0;
}
aRequest.QueryInterface(Ci.nsIChannel);
aRequest.QueryInterface(Ci.nsIWritablePropertyBag);
var contentDispositionFilename;
try {
contentDispositionFilename = aRequest.contentDispositionFilename;
} catch (e) {}
// Change the content type so we don't get stuck in a loop.
aRequest.setProperty('contentType', aRequest.contentType);
aRequest.contentType = 'text/html';
if (isHttpRequest) {
// We trust PDF viewer, using no CSP
aRequest.setResponseHeader('Content-Security-Policy', '', false);
aRequest.setResponseHeader('Content-Security-Policy-Report-Only', '',
false);
aRequest.setResponseHeader('X-Content-Security-Policy', '', false);
aRequest.setResponseHeader('X-Content-Security-Policy-Report-Only', '',
false);
}
PdfJsTelemetry.onViewerIsUsed();
PdfJsTelemetry.onDocumentSize(aRequest.contentLength);
// Creating storage for PDF data
var contentLength = aRequest.contentLength;
this.dataListener = new PdfDataListener(contentLength);
this.binaryStream = Cc['@mozilla.org/binaryinputstream;1']
.createInstance(Ci.nsIBinaryInputStream);
// Create a new channel that is viewer loaded as a resource.
var ioService = Services.io;
var channel = ioService.newChannel(
PDF_VIEWER_WEB_PAGE, null, null);
var listener = this.listener;
var dataListener = this.dataListener;
// Proxy all the request observer calls, when it gets to onStopRequest
// we can get the dom window. We also intentionally pass on the original
// request(aRequest) below so we don't overwrite the original channel and
// trigger an assertion.
var proxy = {
onStartRequest: function(request, context) {
listener.onStartRequest(aRequest, context);
},
onDataAvailable: function(request, context, inputStream, offset, count) {
listener.onDataAvailable(aRequest, context, inputStream, offset, count);
},
onStopRequest: function(request, context, statusCode) {
// We get the DOM window here instead of before the request since it
// may have changed during a redirect.
var domWindow = getDOMWindow(channel);
var actions;
if (rangeRequest) {
actions = new RangedChromeActions(
domWindow, contentDispositionFilename, aRequest, dataListener);
} else {
actions = new StandardChromeActions(
domWindow, contentDispositionFilename, dataListener);
}
var requestListener = new RequestListener(actions);
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
requestListener.receive(event);
}, false, true);
if (actions.supportsIntegratedFind()) {
var chromeWindow = getChromeWindow(domWindow);
var findEventManager = new FindEventManager(chromeWindow.gFindBar,
domWindow,
chromeWindow);
findEventManager.bind();
}
listener.onStopRequest(aRequest, context, statusCode);
}
};
// Keep the URL the same so the browser sees it as the same.
channel.originalURI = aRequest.URI;
channel.loadGroup = aRequest.loadGroup;
// We can use resource principal when data is fetched by the chrome
// e.g. useful for NoScript
var securityManager = Cc['@mozilla.org/scriptsecuritymanager;1']
.getService(Ci.nsIScriptSecurityManager);
var uri = ioService.newURI(PDF_VIEWER_WEB_PAGE, null, null);
// FF16 and below had getCodebasePrincipal, it was replaced by
// getNoAppCodebasePrincipal (bug 758258).
var resourcePrincipal = 'getNoAppCodebasePrincipal' in securityManager ?
securityManager.getNoAppCodebasePrincipal(uri) :
securityManager.getCodebasePrincipal(uri);
aRequest.owner = resourcePrincipal;
channel.asyncOpen(proxy, aContext);
},
// nsIRequestObserver::onStopRequest
onStopRequest: function(aRequest, aContext, aStatusCode) {
if (!this.dataListener) {
// Do nothing
return;
}
if (Components.isSuccessCode(aStatusCode))
this.dataListener.finish();
else
this.dataListener.error(aStatusCode);
delete this.dataListener;
delete this.binaryStream;
}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]);

View File

@ -0,0 +1,18 @@
# Copyright 2012 Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Chrome notification bar messages and buttons
unsupported_feature=Es posible que este documento PDF no se muestre correctamente.
open_with_different_viewer=Abrir con un visor diferente
open_with_different_viewer.accessKey=a

View File

@ -0,0 +1,8 @@
<em:localized>
<Description>
<em:locale>es</em:locale>
<em:name>Visor de PDF</em:name>
<em:description>Usa HTML5 para mostrar archivos PDF directamente en Firefox.</em:description>
</Description>
</em:localized>

View File

@ -0,0 +1,141 @@
# Copyright 2012 Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Main toolbar buttons (tooltips and alt text for images)
previous.title=Página anterior
previous_label=Anterior
next.title=Página siguiente
next_label=Siguiente
# LOCALIZATION NOTE (page_label, page_of):
# These strings are concatenated to form the "Page: X of Y" string.
# Do not translate "{{pageCount}}", it will be substituted with a number
# representing the total number of pages.
page_label=Página:
page_of=de {{pageCount}}
zoom_out.title=Reducir
zoom_out_label=Reducir
zoom_in.title=Aumentar
zoom_in_label=Aumentar
zoom.title=Ampliación
presentation_mode.title=Cambiar al modo de presentación
presentation_mode_label=Modo de presentación
open_file.title=Abrir un archivo
open_file_label=Abrir
print.title=Imprimir
print_label=Imprimir
download.title=Descargar
download_label=Descargar
bookmark.title=Vista actual (para copiar o abrir en otra ventana)
bookmark_label=Vista actual
# Secondary toolbar and context menu
tools.title=Herramientas
tools_label=Herramientas
first_page.title=Ir a la primera página
first_page.label=Ir a la primera página
first_page_label=Ir a la primera página
last_page.title=Ir a la última página
last_page.label=Ir a la última página
last_page_label=Ir a la última página
page_rotate_cw.title=Girar a la derecha
page_rotate_cw.label=Girar a la derecha
page_rotate_cw_label=Girar a la derecha
page_rotate_ccw.title=Girar a la izquierda
page_rotate_ccw.label=Girar a la izquierda
page_rotate_ccw_label=Girar a la izquierda
hand_tool_enable.title=Activar la herramienta Mano
hand_tool_enable_label=Activar la herramienta Mano
hand_tool_disable.title=Desactivar la herramienta Mano
hand_tool_disable_label=Desactivar la herramienta Mano
# Tooltips and alt text for side panel toolbar buttons
# (the _label strings are alt text for the buttons, the .title strings are
# tooltips)
toggle_sidebar.title=Mostrar u ocultar la barra lateral
toggle_sidebar_label=Conmutar la barra lateral
outline.title=Mostrar el esquema del documento
outline_label=Esquema del documento
thumbs.title=Mostrar las miniaturas
thumbs_label=Miniaturas
findbar.title=Buscar en el documento
findbar_label=Buscar
# Thumbnails panel item (tooltip and alt text for images)
# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
# number.
thumb_page_title=Página {{page}}
# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
# number.
thumb_page_canvas=Miniatura de la página {{page}}
# Find panel button title and messages
find_label=Buscar:
find_previous.title=Ir a la frase encontrada anterior
find_previous_label=Anterior
find_next.title=Ir a la frase encontrada siguiente
find_next_label=Siguiente
find_highlight=Resaltar todo
find_match_case_label=Coincidir mayúsculas y minúsculas
find_reached_top=Se alcanzó el inicio del documento, se continúa desde el final
find_reached_bottom=Se alcanzó el final del documento, se continúa desde el inicio
find_not_found=No se encontró la frase
# Error panel labels
error_more_info=Más información
error_less_info=Menos información
error_close=Cerrar
# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
# replaced by the PDF.JS version and build ID.
error_version_info=PDF.js v{{version}} (compilación: {{build}})
# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an
# english string describing the error.
error_message=Mensaje: {{message}}
# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack
# trace.
error_stack=Pila: {{stack}}
# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename
error_file=Archivo: {{file}}
# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number
error_line=Línea: {{line}}
rendering_error=Ocurrió un error al renderizar la página.
# Predefined zoom values
page_scale_width=Anchura de la página
page_scale_fit=Ajustar a la página
page_scale_auto=Ampliación automática
page_scale_actual=Tamaño real
# Loading indicator messages
loading_error_indicator=Error
loading_error=Ocurrió un error al cargar el PDF.
invalid_file_error=El archivo PDF no es válido o está dañado.
missing_file_error=Falta el archivo PDF.
# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
# "{{type}}" will be replaced with an annotation type from a list defined in
# the PDF spec (32000-1:2008 Table 169 Annotation types).
# Some common types are e.g.: "Check", "Text", "Comment", "Note"
text_annotation_type.alt=[Anotación {{type}}]
password_label=Escriba la contraseña para abrir este archivo PDF.
password_invalid=La contraseña no es válida. Inténtelo de nuevo.
password_ok=Aceptar
password_cancel=Cancelar
printing_not_supported=Aviso: Este navegador no es compatible completamente con la impresión.
printing_not_ready=Aviso: El PDF no se ha cargado completamente para su impresión.
web_fonts_disabled=Se han desactivado los tipos de letra web: no se pueden usar los tipos de letra incrustados en el PDF.
document_colors_disabled=No se permite que los documentos PDF usen sus propios colores: la opción «Permitir que las páginas elijan sus propios colores» está desactivada en el navegador.

View File

@ -0,0 +1,19 @@
# Copyright 2012 Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Chrome notification bar messages and buttons
unsupported_feature=Dette PDF-dokumentet vert kanskje ikkje vist rett.
open_with_different_viewer=Opne med eit anna visingsprogram
open_with_different_viewer.accessKey=o

View File

@ -0,0 +1,8 @@
 <em:localized>
<Description>
<em:locale>no</em:locale>
<em:name>PDF Viewer</em:name>
<em:description>Bruker HTML5 for å vise PDF-filer direkte i Firefox.</em:description>
</Description>
</em:localized>

View File

@ -0,0 +1,134 @@
# Copyright 2012 Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Main toolbar buttons (tooltips and alt text for images)
previous.title=Førre Side
previous_label=Førre
next.title=Neste side
next_label=Neste
# LOCALIZATION NOTE (page_label, page_of):
# These strings are concatenated to form the "Page: X of Y" string.
# Do not translate "{{pageCount}}", it will be substituted with a number
# representing the total number of pages.
page_label=Side:
page_of=av {{pageCount}}
zoom_out.title=Zoom ut
zoom_out_label=Zoom ut
zoom_in.title=Zoom inn
zoom_in_label=Zoom inn
zoom.title=Zoom
presentation_mode.title=Bytt til presentasjonsmodus
presentation_mode_label=Presentasjonsmodus
open_file.title=Opne fil
open_file_label=Opne
print.title=Skriv ut
print_label=Skriv ut
download.title=Last ned
download_label=Last ned
bookmark.title=Gjeldande visning (kopier eller opne i nytt vindauge)
bookmark_label=Gjeldende visning
# Secondary toolbar and context menu
tools.title=Verktøy
tools_label=Verktøy
first_page.title=Gå til første side
first_page.label=Gå til første side
first_page_label=Gå til første side
last_page.title=Gå til siste side
last_page.label=Gå til siste side
last_page_label=Gå til siste side
page_rotate_cw.title=Roter med klokka
page_rotate_cw.label=Roter med klokka
page_rotate_cw_label=Roter med klokka
page_rotate_ccw.title=Roter mot klokka
page_rotate_ccw.label=Roter mot klokka
page_rotate_ccw_label=Roter mot klokka
# Tooltips and alt text for side panel toolbar buttons
# (the _label strings are alt text for the buttons, the .title strings are
# tooltips)
toggle_sidebar.title=Veksle Sidebar
toggle_sidebar_label=Veksle Sidebar
outline.title=Vis Document Outline
outline_label=Document Outline
thumbs.title=Vis miniatyrbilder
thumbs_label=Miniatyrbilder
findbar.title=Finne i Dokument
findbar_label=Finn
# Thumbnails panel item (tooltip and alt text for images)
# LOCALIZATION NOTE (thumb_page_title): "{{page}}" will be replaced by the page
# number.
thumb_page_title=Side {{page}}
# LOCALIZATION NOTE (thumb_page_canvas): "{{page}}" will be replaced by the page
# number.
thumb_page_canvas=Thumbnail av siden {{page}}
# Find panel button title and messages
find_label=Finn:
find_previous.title=Finn tidlegare førekomst av frasa
find_previous_label=Førre
find_next.title=Finn neste førekomst av frasa
find_next_label=Neste
find_highlight=Uthev alle
find_match_case_label=Skil store/små bokstavar
find_reached_top=Nådde toppen av dokumentet, held fram frå botnen
find_reached_bottom=Nådde botnen av dokumentet, held fram frå toppen
find_not_found=Fann ikkje teksten
# Error panel labels
error_more_info=Meir info
error_less_info=Mindre info
error_close=Lukk
# LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
# replaced by the PDF.JS version and build ID.
error_version_info=PDF.js v {{version}} (build: {{build}})
# LOCALIZATION NOTE (error_message): "{{message}}" will be replaced by an
# english string describing the error.
error_message=Melding: {{message}}
# LOCALIZATION NOTE (error_stack): "{{stack}}" will be replaced with a stack
# trace.
error_stack=Stakk: {{stack}}
# LOCALIZATION NOTE (error_file): "{{file}}" will be replaced with a filename
error_file=Fil: {{file}}
# LOCALIZATION NOTE (error_line): "{{line}}" will be replaced with a line number
error_line=Linje: {{line}}
rendering_error=Ein feil oppstod ved oppteikning av sida.
# Predefined zoom values
page_scale_width=Sidebreidde
page_scale_fit=Tilpass til sida
page_scale_auto=Automatisk zoom
page_scale_actual=Verkeleg størrelse
# Loading indicator messages
loading_error_indicator=Feil
loading_error=Ein feil oppstod ved lasting av PDF.
invalid_file_error=Ugyldig eller korrupt PDF fil.
missing_file_error=Manglande PDF-fil.
# LOCALIZATION NOTE (text_annotation_type.alt): This is used as a tooltip.
# "{{type}}" will be replaced with an annotation type from a list defined in
# the PDF spec (32000-1:2008 Table 169 Annotation types).
# Some common types are e.g.: "Check", "Text", "Comment", "Note"
text_annotation_type.alt=[{{type}} annotasjon]
request_password=PDF er beskytta av eit passord:
invalid_password=Ugyldig passord.
printing_not_supported=Åtvaring: Utskrift er ikkje fullstendig støtta av denne nettlesaren.
printing_not_ready=Åtvaring: PDF ikkje fullstendig innlasta for utskrift.
web_fonts_disabled=Web-fontar er avslått: Kan ikkje bruke innbundne PDF-fontar.
document_colors_disabled=PDF-dokument har ikkje løyve til å nytte eigne fargar: \'Tillat sider å velje eigne fargar\' er slått av i nettlesaren.

View File

@ -0,0 +1,640 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- vim: set shiftwidth=4 tabstop=4 autoindent noexpandtab: -->
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (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.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is reftest-analyzer.html.
-
- The Initial Developer of the Original Code is the Mozilla Foundation.
- Portions created by the Initial Developer are Copyright (C) 2008
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- L. David Baron <dbaron@dbaron.org>, Mozilla Corporation (original author)
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!--
Features to add:
* make the left and right parts of the viewer independently scrollable
* make the test list filterable
** default to only showing unexpecteds
* add other ways to highlight differences other than circling?
* add zoom/pan to images
* Add ability to load log via XMLHttpRequest (also triggered via URL param)
* color the test list based on pass/fail and expected/unexpected/random/skip
* ability to load multiple logs ?
** rename them by clicking on the name and editing
** turn the test list into a collapsing tree view
** move log loading into popup from viewer UI
-->
<html lang="en-US" xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Reftest analyzer</title>
<style type="text/css"><![CDATA[
html, body { margin: 0; }
html { padding: 0; }
body { padding: 4px; }
#pixelarea, #itemlist, #images { position: absolute; }
#itemlist, #images { overflow: auto; }
#pixelarea { top: 0; left: 0; width: 320px; height: 84px; overflow: visible }
#itemlist { top: 84px; left: 0; width: 320px; bottom: 0; }
#images { top: 0; bottom: 0; left: 320px; right: 0; }
#leftpane { width: 320px; }
#images { position: fixed; top: 10px; left: 340px; }
form#imgcontrols { margin: 0; display: block; }
#itemlist > table { border-collapse: collapse; }
#itemlist > table > tbody > tr > td { border: 1px solid; padding: 1px; }
.selected { background-color: lightsteelblue; }
/*
#itemlist > table > tbody > tr.pass > td.url { background: lime; }
#itemlist > table > tbody > tr.fail > td.url { background: red; }
*/
#magnification > svg { display: block; width: 84px; height: 84px; }
#pixelinfo { font: small sans-serif; position: absolute; width: 200px; left: 84px; }
#pixelinfo table { border-collapse: collapse; }
#pixelinfo table th { white-space: nowrap; text-align: left; padding: 0; }
#pixelinfo table td { font-family: monospace; padding: 0 0 0 0.25em; }
#pixelhint { display: inline; color: #88f; cursor: help; }
#pixelhint > * { display: none; position: absolute; margin: 8px 0 0 8px; padding: 4px; width: 400px; background: #ffa; color: black; box-shadow: 3px 3px 2px #888; z-index: 1; }
#pixelhint:hover { color: #000; }
#pixelhint:hover > * { display: block; }
#pixelhint p { margin: 0; }
#pixelhint p + p { margin-top: 1em; }
]]></style>
<script type="text/javascript"><![CDATA[
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_NS = "http://www.w3.org/2000/svg";
var gPhases = null;
var gIDCache = {};
var gMagPixPaths = []; // 2D array of array-of-two <path> objects used in the pixel magnifier
var gMagWidth = 5; // number of zoomed in pixels to show horizontally
var gMagHeight = 5; // number of zoomed in pixels to show vertically
var gMagZoom = 16; // size of the zoomed in pixels
var gImage1Data; // ImageData object for the test output image
var gImage2Data; // ImageData object for the reference image
var gFlashingPixels = []; // array of <path> objects that should be flashed due to pixel color mismatch
var gPath = ''; // path taken from #web= and prepended to ref/snp urls
var gSelected = null; // currently selected comparison
function ID(id) {
if (!(id in gIDCache))
gIDCache[id] = document.getElementById(id);
return gIDCache[id];
}
function hash_parameters() {
var result = { };
var params = window.location.hash.substr(1).split(/[&;]/);
for (var i = 0; i < params.length; i++) {
var parts = params[i].split("=");
result[parts[0]] = unescape(unescape(parts[1]));
}
return result;
}
function load() {
gPhases = [ ID("entry"), ID("loading"), ID("viewer") ];
build_mag();
var params = hash_parameters();
if (params.log) {
ID("logentry").value = params.log;
log_pasted();
} else if (params.web) {
loadFromWeb(params.web);
}
}
function build_mag() {
var mag = ID("mag");
var r = document.createElementNS(SVG_NS, "rect");
r.setAttribute("x", gMagZoom * -gMagWidth / 2);
r.setAttribute("y", gMagZoom * -gMagHeight / 2);
r.setAttribute("width", gMagZoom * gMagWidth);
r.setAttribute("height", gMagZoom * gMagHeight);
mag.appendChild(r);
mag.setAttribute("transform", "translate(" + (gMagZoom * (gMagWidth / 2) + 1) + "," + (gMagZoom * (gMagHeight / 2) + 1) + ")");
for (var x = 0; x < gMagWidth; x++) {
gMagPixPaths[x] = [];
for (var y = 0; y < gMagHeight; y++) {
var p1 = document.createElementNS(SVG_NS, "path");
p1.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "h" + -gMagZoom + "v" + gMagZoom);
p1.setAttribute("stroke", "black");
p1.setAttribute("stroke-width", "1px");
p1.setAttribute("fill", "#aaa");
var p2 = document.createElementNS(SVG_NS, "path");
p2.setAttribute("d", "M" + ((x - gMagWidth / 2) + 1) * gMagZoom + "," + (y - gMagHeight / 2) * gMagZoom + "v" + gMagZoom + "h" + -gMagZoom);
p2.setAttribute("stroke", "black");
p2.setAttribute("stroke-width", "1px");
p2.setAttribute("fill", "#888");
mag.appendChild(p1);
mag.appendChild(p2);
gMagPixPaths[x][y] = [p1, p2];
}
}
var flashedOn = false;
setInterval(function() {
flashedOn = !flashedOn;
flash_pixels(flashedOn);
}, 500);
}
function show_phase(phaseid) {
for (var i in gPhases) {
var phase = gPhases[i];
phase.style.display = (phase.id == phaseid) ? "" : "none";
}
if (phase == "viewer")
ID("images").style.display = "none";
}
function loadFromWeb(url) {
var lastSlash = url.lastIndexOf('/');
if (lastSlash) {
gPath = url.substring(0, lastSlash + 1);
}
var r = new XMLHttpRequest();
r.open("GET", url);
r.onreadystatechange = function() {
if (r.readyState == 4) {
process_log(r.response);
}
}
r.send(null);
}
function fileentry_changed() {
show_phase("loading");
var input = ID("fileentry");
var files = input.files;
if (files.length > 0) {
// Only handle the first file; don't handle multiple selection.
// The parts of the log we care about are ASCII-only. Since we
// can ignore lines we don't care about, best to read in as
// iso-8859-1, which guarantees we don't get decoding errors.
var fileReader = new FileReader();
fileReader.onload = function(e) {
var log = null;
log = e.target.result;
if (log)
process_log(log);
else
show_phase("entry");
}
fileReader.readAsText(files[0], "iso-8859-1");
}
// So the user can process the same filename again (after
// overwriting the log), clear the value on the form input so we
// will always get an onchange event.
input.value = "";
}
function log_pasted() {
show_phase("loading");
var entry = ID("logentry");
var log = entry.value;
entry.value = "";
process_log(log);
}
var gTestItems;
function process_log(contents) {
var lines = contents.split(/[\r\n]+/);
gTestItems = [];
for (var j in lines) {
var line = lines[j];
var match = line.match(/^(?:NEXT ERROR )?REFTEST (.*)$/);
if (!match)
continue;
line = match[1];
match = line.match(/^(TEST-PASS|TEST-UNEXPECTED-PASS|TEST-KNOWN-FAIL|TEST-UNEXPECTED-FAIL)(\(EXPECTED RANDOM\)|) \| ([^\|]+) \|(.*)/);
if (match) {
var state = match[1];
var random = match[2];
var url = match[3];
var extra = match[4];
gTestItems.push(
{
pass: !state.match(/FAIL$/),
// only one of the following three should ever be true
unexpected: !!state.match(/^TEST-UNEXPECTED/),
random: (random == "(EXPECTED RANDOM)"),
skip: (extra == " (SKIP)"),
url: url,
images: []
});
continue;
}
match = line.match(/^ IMAGE[^:]*: (.*)$/);
if (match) {
var item = gTestItems[gTestItems.length - 1];
item.images.push(match[1]);
}
}
build_viewer();
}
function build_viewer() {
if (gTestItems.length == 0) {
show_phase("entry");
return;
}
var cell = ID("itemlist");
while (cell.childNodes.length > 0)
cell.removeChild(cell.childNodes[cell.childNodes.length - 1]);
var table = document.createElement("table");
var tbody = document.createElement("tbody");
table.appendChild(tbody);
for (var i in gTestItems) {
var item = gTestItems[i];
// XXX skip expected pass items until we have filtering UI
if (item.pass && !item.unexpected)
continue;
var tr = document.createElement("tr");
var rowclass = item.pass ? "pass" : "fail";
var td;
var text;
td = document.createElement("td");
text = "";
if (item.unexpected) { text += "!"; rowclass += " unexpected"; }
if (item.random) { text += "R"; rowclass += " random"; }
if (item.skip) { text += "S"; rowclass += " skip"; }
td.appendChild(document.createTextNode(text));
tr.appendChild(td);
td = document.createElement("td");
td.id = "url" + i;
td.className = "url";
// Only display part of URL after "/mozilla/".
var match = item.url.match(/\/mozilla\/(.*)/);
text = document.createTextNode(match ? match[1] : item.url);
if (item.images.length > 0) {
var a = document.createElement("a");
a.href = "javascript:show_images(" + i + ")";
a.appendChild(text);
td.appendChild(a);
} else {
td.appendChild(text);
}
tr.appendChild(td);
tbody.appendChild(tr);
}
cell.appendChild(table);
show_phase("viewer");
}
function get_image_data(src, whenReady) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = 800;
canvas.height = 1000;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
whenReady(ctx.getImageData(0, 0, 800, 1000));
};
img.src = gPath + src;
}
function show_images(i) {
if (gSelected !== null) {
ID('url' + gSelected).classList.remove('selected');
}
gSelected = i;
ID('url' + gSelected).classList.add('selected');
var item = gTestItems[i];
var cell = ID("images");
ID("image1").style.display = "";
ID("image2").style.display = "none";
ID("diffrect").style.display = "none";
ID("imgcontrols").reset();
ID("image1").setAttributeNS(XLINK_NS, "xlink:href", gPath + item.images[0]);
// Making the href be #image1 doesn't seem to work
ID("feimage1").setAttributeNS(XLINK_NS, "xlink:href", gPath + item.images[0]);
if (item.images.length == 1) {
ID("imgcontrols").style.display = "none";
} else {
ID("imgcontrols").style.display = "";
ID("image2").setAttributeNS(XLINK_NS, "xlink:href", gPath + item.images[1]);
// Making the href be #image2 doesn't seem to work
ID("feimage2").setAttributeNS(XLINK_NS, "xlink:href", gPath + item.images[1]);
}
cell.style.display = "";
get_image_data(item.images[0], function(data) { gImage1Data = data });
get_image_data(item.images[1], function(data) { gImage2Data = data });
}
function show_image(i) {
if (i == 1) {
ID("image1").style.display = "";
ID("image2").style.display = "none";
} else {
ID("image1").style.display = "none";
ID("image2").style.display = "";
}
}
function show_differences(cb) {
ID("diffrect").style.display = cb.checked ? "" : "none";
}
function flash_pixels(on) {
var stroke = on ? "red" : "black";
var strokeWidth = on ? "2px" : "1px";
for (var i = 0; i < gFlashingPixels.length; i++) {
gFlashingPixels[i].setAttribute("stroke", stroke);
gFlashingPixels[i].setAttribute("stroke-width", strokeWidth);
}
}
function cursor_point(evt) {
var m = evt.target.getScreenCTM().inverse();
var p = ID("svg").createSVGPoint();
p.x = evt.clientX;
p.y = evt.clientY;
p = p.matrixTransform(m);
return { x: Math.floor(p.x), y: Math.floor(p.y) };
}
function hex2(i) {
return (i < 16 ? "0" : "") + i.toString(16);
}
function canvas_pixel_as_hex(data, x, y) {
var offset = (y * data.width + x) * 4;
var r = data.data[offset];
var g = data.data[offset + 1];
var b = data.data[offset + 2];
return "#" + hex2(r) + hex2(g) + hex2(b);
}
function hex_as_rgb(hex) {
return "rgb(" + [parseInt(hex.substring(1, 3), 16), parseInt(hex.substring(3, 5), 16), parseInt(hex.substring(5, 7), 16)] + ")";
}
function magnify(evt) {
var { x: x, y: y } = cursor_point(evt);
var centerPixelColor1, centerPixelColor2;
var dx_lo = -Math.floor(gMagWidth / 2);
var dx_hi = Math.floor(gMagWidth / 2);
var dy_lo = -Math.floor(gMagHeight / 2);
var dy_hi = Math.floor(gMagHeight / 2);
flash_pixels(false);
gFlashingPixels = [];
for (var j = dy_lo; j <= dy_hi; j++) {
for (var i = dx_lo; i <= dx_hi; i++) {
var px = x + i;
var py = y + j;
var p1 = gMagPixPaths[i + dx_hi][j + dy_hi][0];
var p2 = gMagPixPaths[i + dx_hi][j + dy_hi][1];
if (px < 0 || py < 0 || px >= 800 || py >= 1000) {
p1.setAttribute("fill", "#aaa");
p2.setAttribute("fill", "#888");
} else {
var color1 = canvas_pixel_as_hex(gImage1Data, x + i, y + j);
var color2 = canvas_pixel_as_hex(gImage2Data, x + i, y + j);
p1.setAttribute("fill", color1);
p2.setAttribute("fill", color2);
if (color1 != color2) {
gFlashingPixels.push(p1, p2);
p1.parentNode.appendChild(p1);
p2.parentNode.appendChild(p2);
}
if (i == 0 && j == 0) {
centerPixelColor1 = color1;
centerPixelColor2 = color2;
}
}
}
}
flash_pixels(true);
show_pixelinfo(x, y, centerPixelColor1, hex_as_rgb(centerPixelColor1), centerPixelColor2, hex_as_rgb(centerPixelColor2));
}
function show_pixelinfo(x, y, pix1rgb, pix1hex, pix2rgb, pix2hex) {
var pixelinfo = ID("pixelinfo");
ID("coords").textContent = [x, y];
ID("pix1hex").textContent = pix1hex;
ID("pix1rgb").textContent = pix1rgb;
ID("pix2hex").textContent = pix2hex;
ID("pix2rgb").textContent = pix2rgb;
}
window.addEventListener('keydown', function keydown(event) {
if (event.which === 84) {
// 't' switch test/ref images
var val = 0;
if (document.querySelector('input[name="which"][value="0"]:checked')) {
val = 1;
}
document.querySelector('input[name="which"][value="' + val + '"]').click();
} else if (event.which === 78 || event.which === 80) {
// 'n' next image, 'p' previous image
var select = gSelected;
if (gSelected === null) {
select = 0;
} else if (event.which === 78) {
select++;
} else {
select--;
}
var length = gTestItems.length;
select = select < 0 ? length - 1 : select >= length ? 0 : select;
show_images(select);
}
});
]]></script>
</head>
<body onload="load()">
<div id="entry">
<h1>Reftest analyzer: load reftest log</h1>
<p>Either paste your log into this textarea:<br />
<textarea cols="80" rows="10" id="logentry"/><br/>
<input type="button" value="Process pasted log" onclick="log_pasted()" /></p>
<p>... or load it from a file:<br/>
<input type="file" id="fileentry" onchange="fileentry_changed()" />
</p>
</div>
<div id="loading" style="display:none">Loading log...</div>
<div id="viewer" style="display:none">
<div id="pixelarea">
<div id="pixelinfo">
<table>
<tbody>
<tr><th>Pixel at:</th><td colspan="2" id="coords"/></tr>
<tr><th>Test:</th><td id="pix1rgb"></td><td id="pix1hex"></td></tr>
<tr><th>Reference:</th><td id="pix2rgb"></td><td id="pix2hex"></td></tr>
</tbody>
</table>
<div>
<div id="pixelhint">
<div>
<p>Move the mouse over the reftest image on the right to show
magnified pixels on the left. The color information above is for
the pixel centered in the magnified view.</p>
<p>Image 1 is shown in the upper triangle of each pixel and Image 2
is shown in the lower triangle.</p>
</div>
</div>
</div>
</div>
<div id="magnification">
<svg xmlns="http://www.w3.org/2000/svg" width="84" height="84" shape-rendering="optimizeSpeed">
<g id="mag"/>
</svg>
</div>
</div>
<div id="itemlist"></div>
<div id="images" style="display:none">
<form id="imgcontrols">
<label><input type="radio" name="which" value="0" onchange="show_image(1)" checked="checked" />Test</label>
<label><input type="radio" name="which" value="1" onchange="show_image(2)" />Reference</label>
<label><input type="checkbox" onchange="show_differences(this)" />Circle differences</label>
</form>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800px" height="1000px" viewbox="0 0 800 1000" id="svg">
<defs>
<!-- use sRGB to avoid loss of data -->
<filter id="showDifferences" x="0%" y="0%" width="100%" height="100%"
style="color-interpolation-filters: sRGB">
<feImage id="feimage1" result="img1" xlink:href="#image1" />
<feImage id="feimage2" result="img2" xlink:href="#image2" />
<!-- inv1 and inv2 are the images with RGB inverted -->
<feComponentTransfer result="inv1" in="img1">
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
<feComponentTransfer result="inv2" in="img2">
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
<!-- w1 will have non-white pixels anywhere that img2
is brighter than img1, and w2 for the reverse.
It would be nice not to have to go through these
intermediate states, but feComposite
type="arithmetic" can't transform the RGB channels
and leave the alpha channel untouched. -->
<feComposite result="w1" in="img1" in2="inv2" operator="arithmetic" k2="1" k3="1" />
<feComposite result="w2" in="img2" in2="inv1" operator="arithmetic" k2="1" k3="1" />
<!-- c1 will have non-black pixels anywhere that img2
is brighter than img1, and c2 for the reverse -->
<feComponentTransfer result="c1" in="w1">
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
<feComponentTransfer result="c2" in="w2">
<feFuncR type="linear" slope="-1" intercept="1" />
<feFuncG type="linear" slope="-1" intercept="1" />
<feFuncB type="linear" slope="-1" intercept="1" />
</feComponentTransfer>
<!-- c will be nonblack (and fully on) for every pixel+component where there are differences -->
<feComposite result="c" in="c1" in2="c2" operator="arithmetic" k2="255" k3="255" />
<!-- a will be opaque for every pixel with differences and transparent for all others -->
<feColorMatrix result="a" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0" />
<!-- a, dilated by 4 pixels -->
<feMorphology result="dila4" in="a" operator="dilate" radius="4" />
<!-- a, dilated by 1 pixel -->
<feMorphology result="dila1" in="a" operator="dilate" radius="1" />
<!-- all the pixels in the 3-pixel dilation of a but not in the 1-pixel dilation of a, to highlight the diffs -->
<feComposite result="highlight" in="dila4" in2="dila1" operator="out" />
<feFlood result="red" flood-color="red" />
<feComposite result="redhighlight" in="red" in2="highlight" operator="in" />
<feFlood result="black" flood-color="black" flood-opacity="0.5" />
<feMerge>
<feMergeNode in="black" />
<feMergeNode in="redhighlight" />
</feMerge>
</filter>
</defs>
<g onmousemove="magnify(evt)">
<image x="0" y="0" width="100%" height="100%" id="image1" />
<image x="0" y="0" width="100%" height="100%" id="image2" />
</g>
<rect id="diffrect" filter="url(#showDifferences)" pointer-events="none" x="0" y="0" width="100%" height="100%" />
</svg>
</div>
</div>
</body>
</html>

954
public/vendor/pdfjs/test/test.py vendored Normal file
View File

@ -0,0 +1,954 @@
# Copyright 2012 Mozilla Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json, platform, os, shutil, sys, subprocess, tempfile, threading
import time, urllib, urllib2, hashlib, re, base64, uuid, socket, errno
import traceback
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn
from optparse import OptionParser
from urlparse import urlparse, parse_qs
from threading import Lock
USAGE_EXAMPLE = "%prog"
# The local web server uses the git repo as the document root.
DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
GIT_CLONE_CHECK = True
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
EQLOG_FILE = 'eq.log'
BROWSERLOG_FILE = 'browser.log'
REFDIR = 'ref'
TEST_SNAPSHOTS = 'test_snapshots'
TMPDIR = 'tmp'
VERBOSE = False
BROWSER_TIMEOUT = 120
SERVER_HOST = "localhost"
lock = Lock()
class TestOptions(OptionParser):
def __init__(self, **kwargs):
OptionParser.__init__(self, **kwargs)
self.add_option("-m", "--masterMode", action="store_true", dest="masterMode",
help="Run the script in master mode.", default=False)
self.add_option("--noPrompts", action="store_true", dest="noPrompts",
help="Uses default answers (intended for CLOUD TESTS only!).", default=False)
self.add_option("--manifestFile", action="store", type="string", dest="manifestFile",
help="A JSON file in the form of test_manifest.json (the default).")
self.add_option("-b", "--browser", action="store", type="string", dest="browser",
help="The path to a single browser (right now, only Firefox is supported).")
self.add_option("--browserManifestFile", action="store", type="string",
dest="browserManifestFile",
help="A JSON file in the form of those found in resources/browser_manifests")
self.add_option("--reftest", action="store_true", dest="reftest",
help="Automatically start reftest showing comparison test failures, if there are any.",
default=False)
self.add_option("--port", action="store", dest="port", type="int",
help="The port the HTTP server should listen on.", default=8080)
self.add_option("--unitTest", action="store_true", dest="unitTest",
help="Run the unit tests.", default=False)
self.add_option("--fontTest", action="store_true", dest="fontTest",
help="Run the font tests.", default=False)
self.add_option("--noDownload", action="store_true", dest="noDownload",
help="Skips test PDFs downloading.", default=False)
self.add_option("--statsFile", action="store", dest="statsFile", type="string",
help="The file where to store stats.", default=None)
self.add_option("--statsDelay", action="store", dest="statsDelay", type="int",
help="The amount of time in milliseconds the browser should wait before starting stats.", default=10000)
self.set_usage(USAGE_EXAMPLE)
def verifyOptions(self, options):
if options.reftest and (options.unitTest or options.fontTest):
self.error("--reftest and --unitTest/--fontTest must not be specified at the same time.")
if options.masterMode and options.manifestFile:
self.error("--masterMode and --manifestFile must not be specified at the same time.")
if not options.manifestFile:
options.manifestFile = DEFAULT_MANIFEST_FILE
if options.browser and options.browserManifestFile:
print "Warning: ignoring browser argument since manifest file was also supplied"
if not options.browser and not options.browserManifestFile:
print "Starting server on port %s." % options.port
if not options.statsFile:
options.statsDelay = 0
return options
def prompt(question):
'''Return True iff the user answered "yes" to |question|.'''
inp = raw_input(question +' [yes/no] > ')
return inp == 'yes'
MIMEs = {
'.css': 'text/css',
'.html': 'text/html',
'.js': 'application/javascript',
'.json': 'application/json',
'.svg': 'image/svg+xml',
'.pdf': 'application/pdf',
'.xhtml': 'application/xhtml+xml',
'.gif': 'image/gif',
'.ico': 'image/x-icon',
'.png': 'image/png',
'.log': 'text/plain',
'.properties': 'text/plain'
}
class State:
browsers = [ ]
manifest = { }
taskResults = { }
remaining = { }
results = { }
done = False
numErrors = 0
numEqFailures = 0
numEqNoSnapshot = 0
numFBFFailures = 0
numLoadFailures = 0
eqLog = None
saveStats = False
stats = [ ]
lastPost = { }
class UnitTestState:
browsers = [ ]
browsersRunning = 0
lastPost = { }
numErrors = 0
numRun = 0
class Result:
def __init__(self, snapshot, failure, page):
self.snapshot = snapshot
self.failure = failure
self.page = page
class TestServer(ThreadingMixIn, HTTPServer):
pass
class TestHandlerBase(BaseHTTPRequestHandler):
# Disable annoying noise by default
def log_request(code=0, size=0):
if VERBOSE:
BaseHTTPRequestHandler.log_request(code, size)
def handle_one_request(self):
try:
BaseHTTPRequestHandler.handle_one_request(self)
except socket.error, v:
if v[0] == errno.ECONNRESET:
# Ignoring connection reset by peer exceptions
if VERBOSE:
print 'Detected connection reset'
elif v[0] == errno.EPIPE:
if VERBOSE:
print 'Detected remote peer disconnected'
elif v[0] == 10053:
if VERBOSE:
print 'An established connection was aborted by the' \
' software in your host machine'
else:
raise
def finish(self,*args,**kw):
# From http://stackoverflow.com/a/14355079/1834797
try:
if not self.wfile.closed:
self.wfile.flush()
self.wfile.close()
except socket.error:
pass
self.rfile.close()
def sendFile(self, path, ext):
self.send_response(200)
self.send_header("Accept-Ranges", "bytes")
self.send_header("Content-Type", MIMEs[ext])
self.send_header("Content-Length", os.path.getsize(path))
self.end_headers()
with open(path, "rb") as f:
self.wfile.write(f.read())
def sendFileRange(self, path, ext, start, end):
file_len = os.path.getsize(path)
if (end is None) or (file_len < end):
end = file_len
if (file_len < start) or (end <= start):
self.send_error(416)
return
chunk_len = end - start
time.sleep(chunk_len / 1000000.0)
self.send_response(206)
self.send_header("Accept-Ranges", "bytes")
self.send_header("Content-Type", MIMEs[ext])
self.send_header("Content-Length", chunk_len)
self.send_header("Content-Range", 'bytes ' + str(start) + '-' + str(end - 1) + '/' + str(file_len))
self.end_headers()
with open(path, "rb") as f:
f.seek(start)
self.wfile.write(f.read(chunk_len))
def do_GET(self):
url = urlparse(self.path)
# Ignore query string
path, _ = urllib.unquote_plus(url.path), url.query
path = os.path.abspath(os.path.realpath(DOC_ROOT + os.sep + path))
prefix = os.path.commonprefix(( path, DOC_ROOT ))
_, ext = os.path.splitext(path.lower())
if url.path == "/favicon.ico":
self.sendFile(os.path.join(DOC_ROOT, "test", "resources", "favicon.ico"), ext)
return
if os.path.isdir(path):
self.sendIndex(url.path, url.query)
return
if not (prefix == DOC_ROOT
and os.path.isfile(path)
and ext in MIMEs):
print path
self.send_error(404)
return
if 'Range' in self.headers:
range_re = re.compile(r"^bytes=(\d+)\-(\d+)?")
parsed_range = range_re.search(self.headers.getheader("Range"))
if parsed_range is None:
self.send_error(501)
return
if VERBOSE:
print 'Range requested %s - %s: %s' % (
parsed_range.group(1), parsed_range.group(2))
start = int(parsed_range.group(1))
if parsed_range.group(2) is None:
self.sendFileRange(path, ext, start, None)
else:
end = int(parsed_range.group(2)) + 1
self.sendFileRange(path, ext, start, end)
return
self.sendFile(path, ext)
class UnitTestHandler(TestHandlerBase):
def sendIndex(self, path, query):
print "send index"
def translateFont(self, base64Data):
self.send_response(200)
self.send_header("Content-Type", "text/xml")
self.end_headers()
data = base64.b64decode(base64Data)
taskId = str(uuid.uuid4())
fontPath = 'ttx/' + taskId + '.otf'
resultPath = 'ttx/' + taskId + '.ttx'
with open(fontPath, "wb") as f:
f.write(data)
# When fontTools used directly, we need to snif ttx file
# to check what version of python is used
ttxPath = ''
for path in os.environ["PATH"].split(os.pathsep):
if os.path.isfile(path + os.sep + "ttx"):
ttxPath = path + os.sep + "ttx"
break
if ttxPath == '':
self.wfile.write("<error>TTX was not found</error>")
return
ttxRunner = ''
with open(ttxPath, "r") as f:
firstLine = f.readline()
if firstLine[:2] == '#!' and firstLine.find('python') > -1:
ttxRunner = firstLine[2:].strip()
with open(os.devnull, "w") as fnull:
if ttxRunner != '':
result = subprocess.call([ttxRunner, ttxPath, fontPath], stdout = fnull)
else:
result = subprocess.call([ttxPath, fontPath], stdout = fnull)
os.remove(fontPath)
if not os.path.isfile(resultPath):
self.wfile.write("<error>Output was not generated</error>")
return
with open(resultPath, "rb") as f:
self.wfile.write(f.read())
os.remove(resultPath)
return
def do_POST(self):
with lock:
url = urlparse(self.path)
numBytes = int(self.headers['Content-Length'])
content = self.rfile.read(numBytes)
# Process special utility requests
if url.path == '/ttx':
self.translateFont(content)
return
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
result = json.loads(content)
browser = result['browser']
UnitTestState.lastPost[browser] = int(time.time())
if url.path == "/tellMeToQuit":
tellAppToQuit(url.path, url.query)
UnitTestState.browsersRunning -= 1
UnitTestState.lastPost[browser] = None
return
elif url.path == '/info':
print result['message']
elif url.path == '/submit_task_results':
status, description = result['status'], result['description']
UnitTestState.numRun += 1
if status == 'TEST-UNEXPECTED-FAIL':
UnitTestState.numErrors += 1
message = status + ' | ' + description + ' | in ' + browser
if 'error' in result:
message += ' | ' + result['error']
print message
else:
print 'Error: uknown action' + url.path
class PDFTestHandler(TestHandlerBase):
def sendIndex(self, path, query):
if not path.endswith("/"):
# we need trailing slash
self.send_response(301)
redirectLocation = path + "/"
if query:
redirectLocation += "?" + query
self.send_header("Location", redirectLocation)
self.end_headers()
return
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.end_headers()
if query == "frame":
self.wfile.write("<html><frameset cols=*,200><frame name=pdf>" +
"<frame src='" + path + "'></frameset></html>")
return
location = os.path.abspath(os.path.realpath(DOC_ROOT + os.sep + path))
self.wfile.write("<html><body><h1>PDFs of " + path + "</h1>\n")
for filename in os.listdir(location):
if filename.lower().endswith('.pdf'):
self.wfile.write("<a href='/web/viewer.html?file=" +
urllib.quote_plus(path + filename, '/') + "' target=pdf>" +
filename + "</a><br>\n")
self.wfile.write("</body></html>")
def do_POST(self):
with lock:
numBytes = int(self.headers['Content-Length'])
self.send_response(200)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
url = urlparse(self.path)
if url.path == "/tellMeToQuit":
tellAppToQuit(url.path, url.query)
return
result = json.loads(self.rfile.read(numBytes))
browser = result['browser']
State.lastPost[browser] = int(time.time())
if url.path == "/info":
print result['message']
return
id = result['id']
failure = result['failure']
round = result['round']
page = result['page']
snapshot = result['snapshot']
taskResults = State.taskResults[browser][id]
taskResults[round].append(Result(snapshot, failure, page))
if State.saveStats:
stat = {
'browser': browser,
'pdf': id,
'page': page,
'round': round,
'stats': result['stats']
}
State.stats.append(stat)
def isTaskDone():
last_page_num = result['lastPageNum']
rounds = State.manifest[id]['rounds']
for round in range(0,rounds):
if not taskResults[round]:
return False
latest_page = taskResults[round][-1]
if not latest_page.page == last_page_num:
return False
return True
if isTaskDone():
# sort the results since they sometimes come in out of order
for results in taskResults:
results.sort(key=lambda result: result.page)
check(State.manifest[id], taskResults, browser,
self.server.masterMode)
# Please oh please GC this ...
del State.taskResults[browser][id]
State.remaining[browser] -= 1
checkIfDone()
def checkIfDone():
State.done = True
for key in State.remaining:
if State.remaining[key] != 0:
State.done = False
return
# Applescript hack to quit Chrome on Mac
def tellAppToQuit(path, query):
if platform.system() != "Darwin":
return
d = parse_qs(query)
path = d['path'][0]
cmd = """osascript<<END
tell application "%s"
quit
end tell
END""" % path
os.system(cmd)
class BaseBrowserCommand(object):
def __init__(self, browserRecord):
self.name = browserRecord["name"]
self.path = browserRecord["path"]
self.tempDir = None
self.process = None
if platform.system() == "Darwin" and (self.path.endswith(".app") or self.path.endswith(".app/")):
self._fixupMacPath()
if not os.path.exists(self.path):
raise Exception("Path to browser '%s' does not exist." % self.path)
def setup(self):
self.tempDir = tempfile.mkdtemp()
self.profileDir = os.path.join(self.tempDir, "profile")
self.browserLog = open(BROWSERLOG_FILE, "w")
def teardown(self):
self.process.terminate()
# If the browser is still running, wait up to ten seconds for it to quit
if self.process and self.process.poll() is None:
checks = 0
while self.process.poll() is None and checks < 20:
checks += 1
time.sleep(.5)
# If it's still not dead, try to kill it
if self.process.poll() is None:
print "Process %s is still running. Killing." % self.name
self.process.kill()
self.process.wait()
if self.tempDir is not None and os.path.exists(self.tempDir):
shutil.rmtree(self.tempDir)
self.browserLog.close()
def start(self, url):
raise Exception("Can't start BaseBrowserCommand")
class FirefoxBrowserCommand(BaseBrowserCommand):
def _fixupMacPath(self):
self.path = os.path.join(self.path, "Contents", "MacOS", "firefox-bin")
def setup(self):
super(FirefoxBrowserCommand, self).setup()
shutil.copytree(os.path.join(DOC_ROOT, "test", "resources", "firefox"),
self.profileDir)
def start(self, url):
cmds = [self.path]
if platform.system() == "Darwin":
cmds.append("-foreground")
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog)
class ChromeBrowserCommand(BaseBrowserCommand):
def _fixupMacPath(self):
self.path = os.path.join(self.path, "Contents", "MacOS", "Google Chrome")
def start(self, url):
cmds = [self.path]
cmds.extend(["--user-data-dir=%s" % self.profileDir,
"--no-first-run", "--disable-sync", url])
self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog)
def makeBrowserCommand(browser):
path = browser["path"].lower()
name = browser["name"]
if name is not None:
name = name.lower()
types = {"firefox": FirefoxBrowserCommand,
"chrome": ChromeBrowserCommand }
command = None
for key in types.keys():
if (name and name.find(key) > -1) or path.find(key) > -1:
command = types[key](browser)
command.name = command.name or key
break
if command is None:
raise Exception("Unrecognized browser: %s" % browser)
return command
def makeBrowserCommands(browserManifestFile):
with open(browserManifestFile) as bmf:
browsers = [makeBrowserCommand(browser) for browser in json.load(bmf)]
return browsers
def downloadLinkedPDF(f):
linkFile = open(f +'.link')
link = linkFile.read()
linkFile.close()
sys.stdout.write('Downloading '+ link +' to '+ f +' ...')
sys.stdout.flush()
response = urllib2.urlopen(link)
with open(f, 'wb') as out:
out.write(response.read())
print 'done'
def downloadLinkedPDFs(manifestList):
for item in manifestList:
f, isLink = item['file'], item.get('link', False)
if isLink and not os.access(f, os.R_OK):
try:
downloadLinkedPDF(f)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
print 'ERROR: Unable to download file "' + f + '".'
open(f, 'wb').close()
with open(f + '.error', 'w') as out:
out.write('\n'.join(traceback.format_exception(exc_type,
exc_value,
exc_traceback)))
def verifyPDFs(manifestList):
error = False
for item in manifestList:
f = item['file']
if os.path.isfile(f + '.error'):
print 'WARNING: File was not downloaded. See "' + f + '.error" file.'
error = True
elif os.access(f, os.R_OK):
fileMd5 = hashlib.md5(open(f, 'rb').read()).hexdigest()
if 'md5' not in item:
print 'WARNING: Missing md5 for file "' + f + '".',
print 'Hash for current file is "' + fileMd5 + '"'
error = True
continue
md5 = item['md5']
if fileMd5 != md5:
print 'WARNING: MD5 of file "' + f + '" does not match file.',
print 'Expected "' + md5 + '" computed "' + fileMd5 + '"'
error = True
continue
else:
print 'WARNING: Unable to open file for reading "' + f + '".'
error = True
return not error
def getTestBrowsers(options):
testBrowsers = []
if options.browserManifestFile:
testBrowsers = makeBrowserCommands(options.browserManifestFile)
elif options.browser:
testBrowsers = [makeBrowserCommand({"path":options.browser, "name":None})]
if options.browserManifestFile or options.browser:
assert len(testBrowsers) > 0
return testBrowsers
def setUp(options):
# Only serve files from a pdf.js clone
assert not GIT_CLONE_CHECK or os.path.isfile('../src/pdf.js') and os.path.isdir('../.git')
if options.masterMode and os.path.isdir(TMPDIR):
print 'Temporary snapshot dir tmp/ is still around.'
print 'tmp/ can be removed if it has nothing you need.'
if options.noPrompts or prompt('SHOULD THIS SCRIPT REMOVE tmp/? THINK CAREFULLY'):
subprocess.call(( 'rm', '-rf', 'tmp' ))
assert not os.path.isdir(TMPDIR)
testBrowsers = getTestBrowsers(options)
with open(options.manifestFile) as mf:
manifestList = json.load(mf)
if not options.noDownload:
downloadLinkedPDFs(manifestList)
if not verifyPDFs(manifestList):
print 'Unable to verify the checksum for the files that are used for testing.'
print 'Please re-download the files, or adjust the MD5 checksum in the manifest for the files listed above.\n'
for b in testBrowsers:
State.taskResults[b.name] = { }
State.remaining[b.name] = len(manifestList)
State.lastPost[b.name] = int(time.time())
for item in manifestList:
id, rounds = item['id'], int(item['rounds'])
State.manifest[id] = item
taskResults = [ ]
for r in xrange(rounds):
taskResults.append([ ])
State.taskResults[b.name][id] = taskResults
if options.statsFile != None:
State.saveStats = True
return testBrowsers
def setUpUnitTests(options):
# Only serve files from a pdf.js clone
assert not GIT_CLONE_CHECK or os.path.isfile('../src/pdf.js') and os.path.isdir('../.git')
testBrowsers = getTestBrowsers(options)
UnitTestState.browsersRunning = len(testBrowsers)
for b in testBrowsers:
UnitTestState.lastPost[b.name] = int(time.time())
return testBrowsers
def startBrowsers(browsers, options, path):
for b in browsers:
b.setup()
print 'Launching', b.name
host = 'http://%s:%s' % (SERVER_HOST, options.port)
qs = '?browser='+ urllib.quote(b.name) +'&manifestFile='+ urllib.quote(options.manifestFile)
qs += '&path=' + b.path
qs += '&delay=' + str(options.statsDelay)
qs += '&masterMode=' + str(options.masterMode)
b.start(host + path + qs)
def teardownBrowsers(browsers):
for b in browsers:
try:
b.teardown()
except:
print "Error cleaning up after browser at ", b.path
print "Temp dir was ", b.tempDir
print "Error:", sys.exc_info()[0]
def check(task, results, browser, masterMode):
failed = False
for r in xrange(len(results)):
pageResults = results[r]
for p in xrange(len(pageResults)):
pageResult = pageResults[p]
if pageResult is None:
continue
failure = pageResult.failure
if failure:
failed = True
if os.path.isfile(task['file'] + '.error'):
print 'TEST-SKIPPED | PDF was not downloaded', task['id'], '| in', browser, '| page', p + 1, 'round', r, '|', failure
else:
State.numErrors += 1
print 'TEST-UNEXPECTED-FAIL | test failed', task['id'], '| in', browser, '| page', p + 1, 'round', r, '|', failure
if failed:
return
kind = task['type']
if 'eq' == kind or 'text' == kind:
checkEq(task, results, browser, masterMode)
elif 'fbf' == kind:
checkFBF(task, results, browser)
elif 'load' == kind:
checkLoad(task, results, browser)
else:
assert 0 and 'Unknown test type'
def createDir(dir):
try:
os.makedirs(dir)
except OSError, e:
if e.errno != 17: # file exists
print >>sys.stderr, 'Creating', dir, 'failed!'
def readDataUri(data):
metadata, encoded = data.rsplit(",", 1)
return base64.b64decode(encoded)
def checkEq(task, results, browser, masterMode):
pfx = os.path.join(REFDIR, sys.platform, browser, task['id'])
testSnapshotDir = os.path.join(TEST_SNAPSHOTS, sys.platform, browser, task['id'])
results = results[0]
taskId = task['id']
taskType = task['type']
passed = True
for result in results:
page = result.page
snapshot = readDataUri(result.snapshot)
ref = None
eq = True
path = os.path.join(pfx, str(page) + '.png')
if not os.access(path, os.R_OK):
State.numEqNoSnapshot += 1
if not masterMode:
print 'WARNING: no reference snapshot', path
else:
f = open(path, 'rb')
ref = f.read()
f.close()
eq = (ref == snapshot)
if not eq:
print 'TEST-UNEXPECTED-FAIL |', taskType, taskId, '| in', browser, '| rendering of page', page, '!= reference rendering'
if not State.eqLog:
State.eqLog = open(EQLOG_FILE, 'w')
eqLog = State.eqLog
createDir(testSnapshotDir)
testSnapshotPath = os.path.join(testSnapshotDir, str(page) + '.png')
handle = open(testSnapshotPath, 'wb')
handle.write(snapshot)
handle.close()
refSnapshotPath = os.path.join(testSnapshotDir, str(page) + '_ref.png')
handle = open(refSnapshotPath, 'wb')
handle.write(ref)
handle.close()
# NB: this follows the format of Mozilla reftest
# output so that we can reuse its reftest-analyzer
# script
eqLog.write('REFTEST TEST-UNEXPECTED-FAIL | ' + browser +'-'+ taskId +'-page'+ str(page) + ' | image comparison (==)\n')
eqLog.write('REFTEST IMAGE 1 (TEST): ' + testSnapshotPath + '\n')
eqLog.write('REFTEST IMAGE 2 (REFERENCE): ' + refSnapshotPath + '\n')
passed = False
State.numEqFailures += 1
if masterMode and (ref is None or not eq):
tmpTaskDir = os.path.join(TMPDIR, sys.platform, browser, task['id'])
createDir(tmpTaskDir)
handle = open(os.path.join(tmpTaskDir, str(page)) + '.png', 'wb')
handle.write(snapshot)
handle.close()
if passed:
print 'TEST-PASS |', taskType, 'test', task['id'], '| in', browser
def checkFBF(task, results, browser):
round0, round1 = results[0], results[1]
assert len(round0) == len(round1)
passed = True
for page in xrange(len(round1)):
r0Page, r1Page = round0[page], round1[page]
if r0Page is None:
break
if r0Page.snapshot != r1Page.snapshot:
print 'TEST-UNEXPECTED-FAIL | forward-back-forward test', task['id'], '| in', browser, '| first rendering of page', page + 1, '!= second'
passed = False
State.numFBFFailures += 1
if passed:
print 'TEST-PASS | forward-back-forward test', task['id'], '| in', browser
def checkLoad(task, results, browser):
# Load just checks for absence of failure, so if we got here the
# test has passed
print 'TEST-PASS | load test', task['id'], '| in', browser
def processResults(options):
print ''
numFatalFailures = (State.numErrors + State.numFBFFailures)
if 0 == State.numEqFailures and 0 == numFatalFailures:
print 'All regression tests passed.'
else:
print 'OHNOES! Some tests failed!'
if 0 < State.numErrors:
print ' errors:', State.numErrors
if 0 < State.numEqFailures:
print ' different ref/snapshot:', State.numEqFailures
if 0 < State.numFBFFailures:
print ' different first/second rendering:', State.numFBFFailures
if options.statsFile != None:
with open(options.statsFile, 'w') as sf:
sf.write(json.dumps(State.stats, sort_keys=True, indent=4))
print 'Wrote stats file: ' + options.statsFile
def maybeUpdateRefImages(options, browser):
if options.masterMode and (0 < State.numEqFailures or 0 < State.numEqNoSnapshot):
print "Some eq tests failed or didn't have snapshots."
print 'Checking to see if master references can be updated...'
numFatalFailures = (State.numErrors + State.numFBFFailures)
if 0 < numFatalFailures:
print ' No. Some non-eq tests failed.'
else:
print ' Yes! The references in tmp/ can be synced with ref/.'
if options.reftest:
startReftest(browser, options)
if options.noPrompts or prompt('Would you like to update the master copy in ref/?'):
sys.stdout.write(' Updating ref/ ... ')
if not os.path.exists('ref'):
subprocess.check_call('mkdir ref', shell = True)
subprocess.check_call('cp -Rf tmp/* ref/', shell = True)
print 'done'
else:
print ' OK, not updating.'
def startReftest(browser, options):
url = "http://%s:%s" % (SERVER_HOST, options.port)
url += "/test/resources/reftest-analyzer.xhtml"
url += "#web=/test/eq.log"
try:
browser.setup()
browser.start(url)
print "Waiting for browser..."
browser.process.wait()
finally:
teardownBrowsers([browser])
print "Completed reftest usage."
def runTests(options, browsers):
try:
shutil.rmtree(TEST_SNAPSHOTS);
except OSError, e:
if e.errno != 2: # folder doesn't exist
print >>sys.stderr, 'Deleting', dir, 'failed!'
t1 = time.time()
try:
startBrowsers(browsers, options, '/test/test_slave.html')
while not State.done:
for b in State.lastPost:
if State.remaining[b] > 0 and int(time.time()) - State.lastPost[b] > BROWSER_TIMEOUT:
print 'TEST-UNEXPECTED-FAIL | test failed', b, "has not responded in", BROWSER_TIMEOUT, "s"
State.numErrors += State.remaining[b]
State.remaining[b] = 0
checkIfDone()
time.sleep(1)
processResults(options)
finally:
teardownBrowsers(browsers)
t2 = time.time()
print "Runtime was", int(t2 - t1), "seconds"
if State.eqLog:
State.eqLog.close();
if options.masterMode:
maybeUpdateRefImages(options, browsers[0])
elif options.reftest and State.numEqFailures > 0:
print "\nStarting reftest harness to examine %d eq test failures." % State.numEqFailures
startReftest(browsers[0], options)
def runUnitTests(options, browsers, url, name):
t1 = time.time()
try:
startBrowsers(browsers, options, url)
while UnitTestState.browsersRunning > 0:
for b in UnitTestState.lastPost:
if UnitTestState.lastPost[b] != None and int(time.time()) - UnitTestState.lastPost[b] > BROWSER_TIMEOUT:
print 'TEST-UNEXPECTED-FAIL | test failed', b, "has not responded in", BROWSER_TIMEOUT, "s"
UnitTestState.lastPost[b] = None
UnitTestState.browsersRunning -= 1
UnitTestState.numErrors += 1
time.sleep(1)
print ''
print 'Ran', UnitTestState.numRun, 'tests'
if UnitTestState.numErrors > 0:
print 'OHNOES! Some', name, 'tests failed!'
print ' ', UnitTestState.numErrors, 'of', UnitTestState.numRun, 'failed'
else:
print 'All', name, 'tests passed.'
finally:
teardownBrowsers(browsers)
t2 = time.time()
print '', name, 'tests runtime was', int(t2 - t1), 'seconds'
def main():
optionParser = TestOptions()
options, args = optionParser.parse_args()
options = optionParser.verifyOptions(options)
if options == None:
sys.exit(1)
if options.unitTest or options.fontTest:
httpd = TestServer((SERVER_HOST, options.port), UnitTestHandler)
httpd_thread = threading.Thread(target=httpd.serve_forever)
httpd_thread.setDaemon(True)
httpd_thread.start()
browsers = setUpUnitTests(options)
if len(browsers) > 0:
if options.unitTest:
runUnitTests(options, browsers, '/test/unit/unit_test.html', 'unit')
if options.fontTest:
runUnitTests(options, browsers, '/test/font/font_test.html', 'font')
else:
httpd = TestServer((SERVER_HOST, options.port), PDFTestHandler)
httpd.masterMode = options.masterMode
httpd_thread = threading.Thread(target=httpd.serve_forever)
httpd_thread.setDaemon(True)
httpd_thread.start()
browsers = setUp(options)
if len(browsers) > 0:
runTests(options, browsers)
else:
# just run the server
print "Running HTTP server. Press Ctrl-C to quit."
try:
while True:
time.sleep(1)
except (KeyboardInterrupt):
print "\nExiting."
if __name__ == '__main__':
main()

BIN
public/vendor/pdfjs/web/images/Thumbs.db vendored Normal file

Binary file not shown.

33
public/vendor/sizzle/.bower.json vendored Normal file
View File

@ -0,0 +1,33 @@
{
"name": "sizzle",
"version": "1.10.16",
"main": "./dist/sizzle.js",
"devDependencies": {
"qunit": "~1.12.0",
"benchmark": "~1.0.0",
"requirejs": "~2.1.8",
"requirejs-domready": "~2.0.1",
"requirejs-text": "~2.0.10"
},
"ignore": [
"**/.*",
"package.json",
"bower.json",
"speed",
"Makefile",
"*.md",
"*.txt",
"src",
"Gruntfile.js"
],
"homepage": "https://github.com/jquery/sizzle",
"_release": "1.10.16",
"_resolution": {
"type": "version",
"tag": "1.10.16",
"commit": "0fd151739d05648118002914c7a638411bbd0dbe"
},
"_source": "git://github.com/jquery/sizzle.git",
"_target": "1.10.16",
"_originalSource": "sizzle"
}

2015
public/vendor/sizzle/dist/sizzle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10
public/vendor/sizzle/tasks/commit.js vendored Normal file
View File

@ -0,0 +1,10 @@
"use strict";
var exec = require( "child_process" ).exec;
module.exports = function( grunt ) {
grunt.registerTask( "commit", "Add and commit changes", function( message ) {
// Always add dist directory
exec( "git add dist && git commit -m " + message, this.async() );
});
};

34
public/vendor/sizzle/tasks/compile.js vendored Normal file
View File

@ -0,0 +1,34 @@
"use strict";
module.exports = function( grunt ) {
grunt.registerMultiTask(
"compile",
"Compile sizzle.js to the dist directory. Embed date/version.",
function() {
var data = this.data,
dest = data.dest,
src = data.src,
version = grunt.config( "pkg.version" ),
compiled = grunt.file.read( src );
// Embed version and date
compiled = compiled
.replace( /@VERSION/g, version )
.replace( "@DATE", function() {
var date = new Date();
// YYYY-MM-DD
return [
date.getFullYear(),
( "0" + ( date.getMonth() + 1 ) ).slice( -2 ),
( "0" + date.getDate() ).slice( -2 )
].join( "-" );
});
// Write source to file
grunt.file.write( dest, compiled );
grunt.log.ok( "File written to " + dest );
}
);
};

35
public/vendor/sizzle/tasks/dist.js vendored Normal file
View File

@ -0,0 +1,35 @@
"use strict";
var fs = require( "fs" );
module.exports = function( grunt ) {
grunt.registerTask( "dist", "Process files for distribution", function() {
var files = grunt.file.expand( { filter: "isFile" }, "dist/*" );
files.forEach(function( filename ) {
var map,
text = fs.readFileSync( filename, "utf8" );
// Modify map/min so that it points to files in the same folder;
// see https://github.com/mishoo/UglifyJS2/issues/47
if ( /\.map$/.test( filename ) ) {
text = text.replace( /"dist\//g, "\"" );
fs.writeFileSync( filename, text, "utf-8" );
} else if ( /\.min\.js$/.test( filename ) ) {
// Wrap sourceMap directive in multiline comments (#13274)
text = text.replace( /\n?(\/\/@\s*sourceMappingURL=)(.*)/,
function( _, directive, path ) {
map = "\n" + directive + path.replace( /^dist\//, "" );
return "";
});
if ( map ) {
text = text.replace( /(^\/\*[\w\W]*?)\s*\*\/|$/,
function( _, comment ) {
return ( comment || "\n/*" ) + map + "\n*/";
});
}
fs.writeFileSync( filename, text, "utf-8" );
}
});
});
};

43
public/vendor/sizzle/tasks/release.js vendored Normal file
View File

@ -0,0 +1,43 @@
"use strict";
var exec = require( "child_process" ).exec;
module.exports = function( grunt ) {
var rpreversion = /(\d\.\d+\.\d+)-pre/;
grunt.registerTask( "release",
"Release a version of sizzle, updates a pre version to released, " +
"inserts `next` as the new pre version", function( next ) {
if ( !rpreversion.test( next ) ) {
grunt.fatal( "Next version should be a -pre version (x.x.x-pre): " + next );
return;
}
var done,
version = grunt.config( "pkg.version" );
if ( !rpreversion.test( version ) ) {
grunt.fatal( "Existing version is not a pre version: " + version );
return;
}
version = version.replace( rpreversion, "$1" );
done = this.async();
exec( "git diff --quiet HEAD", function( err ) {
if ( err ) {
grunt.fatal( "The working directory should be clean when releasing. Commit or stash changes." );
return;
}
// Build to dist directories along with a map and tag the release
grunt.task.run([
// Commit new version
"version:" + version,
// Tag new version
"tag:" + version,
// Commit next version
"version:" + next
]);
done();
});
});
};

9
public/vendor/sizzle/tasks/tag.js vendored Normal file
View File

@ -0,0 +1,9 @@
"use strict";
var exec = require( "child_process" ).exec;
module.exports = function( grunt ) {
grunt.registerTask( "tag", "Tag the specified version", function( version ) {
exec( "git tag " + version, this.async() );
});
};

35
public/vendor/sizzle/tasks/version.js vendored Normal file
View File

@ -0,0 +1,35 @@
"use strict";
var exec = require( "child_process" ).exec;
module.exports = function( grunt ) {
grunt.registerTask( "version", "Commit a new version", function( version ) {
if ( !/\d\.\d+\.\d+(?:-pre)?/.test( version ) ) {
grunt.fatal( "Version must follow semver release format: " + version );
return;
}
var done = this.async(),
files = grunt.config( "version.files" ),
rversion = /("version":\s*")[^"]+/;
// Update version in specified files
files.forEach(function( filename ) {
var text = grunt.file.read( filename );
text = text.replace( rversion, "$1" + version );
grunt.file.write( filename, text );
});
// Add files to git index
exec( "git add -A", function( err ) {
if ( err ) {
grunt.fatal( err );
return;
}
// Commit next pre version
grunt.config( "pkg.version", version );
grunt.task.run([ "build", "uglify", "dist", "commit:'Update version to " + version + "'" ]);
done();
});
});
};

View File

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script>var QUnit = parent.QUnit</script>
<script src="testinit.js"></script>
<script src="../../dist/sizzle.js"></script>
</head>
<body>
<script>
var doc = parent.document,
unframed = [ doc.getElementById( "qunit-fixture" ), doc.body, doc.documentElement ],
framed = Sizzle( "*" );
window.parent.iframeCallback(
Sizzle.uniqueSort( unframed.concat( framed ) ),
framed.concat( unframed.reverse() ),
"Mixed array was sorted correctly"
);
</script>
</body>
</html>

View File

@ -0,0 +1,136 @@
var fireNative,
jQuery = this.jQuery || "jQuery", // For testing .noConflict()
$ = this.$ || "$",
originaljQuery = jQuery,
original$ = $;
(function() {
// Config parameter to force basic code paths
QUnit.config.urlConfig.push({
id: "basic",
label: "Bypass optimizations",
tooltip: "Force use of the most basic code by disabling native querySelectorAll; contains; compareDocumentPosition"
});
if ( QUnit.urlParams.basic ) {
document.querySelectorAll = null;
document.documentElement.contains = null;
document.documentElement.compareDocumentPosition = null;
// Return array of length two to pass assertion
// But support should be false as its not native
document.getElementsByClassName = function() { return [ 0, 1 ]; };
}
})();
/**
* Returns an array of elements with the given IDs
* @example q("main", "foo", "bar")
* @result [<div id="main">, <span id="foo">, <input id="bar">]
*/
function q() {
var r = [],
i = 0;
for ( ; i < arguments.length; i++ ) {
r.push( document.getElementById( arguments[i] ) );
}
return r;
}
/**
* Asserts that a select matches the given IDs
* @param {String} a - Assertion name
* @param {String} b - Sizzle selector
* @param {String} c - Array of ids to construct what is expected
* @example t("Check for something", "//[a]", ["foo", "baar"]);
* @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar'
*/
function t( a, b, c ) {
var f = Sizzle(b),
s = "",
i = 0;
for ( ; i < f.length; i++ ) {
s += ( s && "," ) + '"' + f[ i ].id + '"';
}
deepEqual(f, q.apply( q, c ), a + " (" + b + ")");
}
/**
* Add random number to url to stop caching
*
* @example url("data/test.html")
* @result "data/test.html?10538358428943"
*
* @example url("data/test.php?foo=bar")
* @result "data/test.php?foo=bar&10538358345554"
*/
function url( value ) {
return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000);
}
var createWithFriesXML = function() {
var string = '<?xml version="1.0" encoding="UTF-8"?> \
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" \
xmlns:xsd="http://www.w3.org/2001/XMLSchema" \
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> \
<soap:Body> \
<jsconf xmlns="http://www.example.com/ns1"> \
<response xmlns:ab="http://www.example.com/ns2"> \
<meta> \
<component id="seite1" class="component"> \
<properties xmlns:cd="http://www.example.com/ns3"> \
<property name="prop1"> \
<thing /> \
<value>1</value> \
</property> \
<property name="prop2"> \
<thing att="something" /> \
</property> \
<foo_bar>foo</foo_bar> \
</properties> \
</component> \
</meta> \
</response> \
</jsconf> \
</soap:Body> \
</soap:Envelope>';
return jQuery.parseXML( string );
};
fireNative = document.createEvent ?
function( node, type ) {
var event = document.createEvent("HTMLEvents");
event.initEvent( type, true, true );
node.dispatchEvent( event );
} :
function( node, type ) {
var event = document.createEventObject();
node.fireEvent( "on" + type, event );
};
function testIframeWithCallback( title, fileName, func ) {
test( title, function() {
var iframe;
stop();
window.iframeCallback = function() {
var self = this,
args = arguments;
setTimeout(function() {
window.iframeCallback = undefined;
iframe.remove();
func.apply( self, args );
func = function() {};
start();
}, 0 );
};
iframe = jQuery( "<div/>" ).css({ position: "absolute", width: "500px", left: "-600px" })
.append( jQuery( "<iframe/>" ).attr( "src", url( "./data/" + fileName ) ) )
.appendTo( "#qunit-fixture" );
});
};
window.iframeCallback = undefined;
function moduleTeardown() {}

242
public/vendor/sizzle/test/index.html vendored Normal file
View File

@ -0,0 +1,242 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr" id="html">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Sizzle Test Suite</title>
<link rel="Stylesheet" media="screen" href="libs/qunit/qunit.css" />
<script type="text/javascript" src="libs/qunit/qunit.js"></script>
<script type="text/javascript" src="data/testinit.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="../dist/sizzle.js"></script>
<script type="text/javascript" src="unit/selector.js"></script>
<script type="text/javascript" src="unit/utilities.js"></script>
<script type="text/javascript" src="unit/extending.js"></script>
</head>
<body id="body">
<div id="qunit"></div>
<!-- Test HTML -->
<dl id="dl" style="position:absolute;top:-32767px;left:-32767px;width:1px">
<div id="qunit-fixture">
<p id="firstp">See <a id="simon1" href="http://simon.incutio.com/archive/2003/03/25/#getElementsBySelector" rel="bookmark">this blog entry</a> for more information.</p>
<p id="ap">
Here are some [links] in a normal paragraph: <a id="google" href="http://www.google.com/" title="Google!">Google</a>,
<a id="groups" href="http://groups.google.com/" class="GROUPS">Google Groups (Link)</a>.
This link has <code id="code1"><a href="http://smin" id="anchor1">class="blog"</a></code>:
<a href="http://diveintomark.org/" class="blog" hreflang="en" id="mark">diveintomark</a>
</p>
<div id="foo">
<p id="sndp">Everything inside the red border is inside a div with <code>id="foo"</code>.</p>
<p lang="en" id="en">This is a normal link: <a id="yahoo" href="http://www.yahoo.com/" class="blogTest">Yahoo</a></p>
<p id="sap">This link has <code><a href="#2" id="anchor2">class="blog"</a></code>: <a href="http://simon.incutio.com/" class="blog link" id="simon">Simon Willison's Weblog</a></p>
</div>
<div id="nothiddendiv" style="height:1px;background:white;" class="nothiddendiv">
<div id="nothiddendivchild"></div>
</div>
<span id="name+value"></span>
<p id="first">Try them out:</p>
<ul id="firstUL"></ul>
<ol id="empty"><!-- comment --></ol>
<form id="form" action="formaction">
<label for="action" id="label-for">Action:</label>
<input type="text" name="action" value="Test" id="text1" maxlength="30"/>
<input type="text" name="text2" value="Test" id="text2" disabled="disabled"/>
<input type="radio" name="radio1" id="radio1" value="on"/>
<input type="radio" name="radio2" id="radio2" checked="checked"/>
<input type="checkbox" name="check" id="check1" checked="checked"/>
<input type="checkbox" id="check2" value="on"/>
<input type="hidden" name="hidden" id="hidden1"/>
<input type="text" style="display:none;" name="foo[bar]" id="hidden2"/>
<input type="text" id="name" name="name" value="name" />
<input type="search" id="search" name="search" value="search" />
<button id="button" name="button" type="button">Button</button>
<textarea id="area1" maxlength="30">foobar</textarea>
<select name="select1" id="select1">
<option id="option1a" class="emptyopt" value="">Nothing</option>
<option id="option1b" value="1">1</option>
<option id="option1c" value="2">2</option>
<option id="option1d" value="3">3</option>
</select>
<select name="select2" id="select2">
<option id="option2a" class="emptyopt" value="">Nothing</option>
<option id="option2b" value="1">1</option>
<option id="option2c" value="2">2</option>
<option id="option2d" selected="selected" value="3">3</option>
</select>
<select name="select3" id="select3" multiple="multiple">
<option id="option3a" class="emptyopt" value="">Nothing</option>
<option id="option3b" selected="selected" value="1">1</option>
<option id="option3c" selected="selected" value="2">2</option>
<option id="option3d" value="3">3</option>
<option id="option3e">no value</option>
</select>
<select name="select4" id="select4" multiple="multiple">
<optgroup disabled="disabled">
<option id="option4a" class="emptyopt" value="">Nothing</option>
<option id="option4b" disabled="disabled" selected="selected" value="1">1</option>
<option id="option4c" selected="selected" value="2">2</option>
</optgroup>
<option selected="selected" disabled="disabled" id="option4d" value="3">3</option>
<option id="option4e">no value</option>
</select>
<select name="select5" id="select5">
<option id="option5a" value="3">1</option>
<option id="option5b" value="2">2</option>
<option id="option5c" value="1">3</option>
</select>
<object id="object1" codebase="stupid">
<param name="p1" value="x1" />
<param name="p2" value="x2" />
</object>
<span id="台北Táiběi"></span>
<span id="台北" lang="中文"></span>
<span id="utf8class1" class="台北Táiběi 台北"></span>
<span id="utf8class2" class="台北"></span>
<span id="foo:bar" class="foo:bar"><span id="foo_descendent"></span></span>
<span id="test.foo[5]bar" class="test.foo[5]bar"></span>
<foo_bar id="foobar">test element</foo_bar>
</form>
<b id="floatTest">Float test.</b>
<iframe id="iframe" name="iframe"></iframe>
<form id="lengthtest">
<input type="text" id="length" name="test"/>
<input type="text" id="idTest" name="id"/>
</form>
<table id="table"></table>
<form id="name-tests">
<!-- Inputs with a grouped name attribute. -->
<input name="types[]" id="types_all" type="checkbox" value="all" />
<input name="types[]" id="types_anime" type="checkbox" value="anime" />
<input name="types[]" id="types_movie" type="checkbox" value="movie" />
</form>
<form id="testForm" action="#" method="get">
<textarea name="T3" rows="2" cols="15">?
Z</textarea>
<input type="hidden" name="H1" value="x" />
<input type="hidden" name="H2" />
<input name="PWD" type="password" value="" />
<input name="T1" type="text" />
<input name="T2" type="text" value="YES" readonly="readonly" />
<input type="checkbox" name="C1" value="1" />
<input type="checkbox" name="C2" />
<input type="radio" name="R1" value="1" />
<input type="radio" name="R1" value="2" />
<input type="text" name="My Name" value="me" />
<input type="reset" name="reset" value="NO" />
<select name="S1">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S2" multiple="multiple" size="3">
<option value="abc">ABC</option>
<option value="abc">ABC</option>
<option value="abc">ABC</option>
</select>
<select name="S3">
<option selected="selected">YES</option>
</select>
<select name="S4">
<option value="" selected="selected">NO</option>
</select>
<input type="submit" name="sub1" value="NO" />
<input type="submit" name="sub2" value="NO" />
<input type="image" name="sub3" value="NO" />
<button name="sub4" type="submit" value="NO">NO</button>
<input name="D1" type="text" value="NO" disabled="disabled" />
<input type="checkbox" checked="checked" disabled="disabled" name="D2" value="NO" />
<input type="radio" name="D3" value="NO" checked="checked" disabled="disabled" />
<select name="D4" disabled="disabled">
<option selected="selected" value="NO">NO</option>
</select>
<input id="list-test" type="text" />
<datalist id="datalist">
<option value="option"></option>
</datalist>
</form>
<div id="moretests">
<form>
<div id="checkedtest" style="display:none;">
<input type="radio" name="checkedtestradios" checked="checked"/>
<input type="radio" name="checkedtestradios" value="on"/>
<input type="checkbox" name="checkedtestcheckboxes" checked="checked"/>
<input type="checkbox" name="checkedtestcheckboxes" />
</div>
</form>
<div id="nonnodes"><span>hi</span> there <!-- mon ami --></div>
<div id="t2037">
<div><div class="hidden">hidden</div></div>
</div>
<div id="t6652">
<div></div>
</div>
<div id="t12087">
<input type="hidden" id="el12087" data-comma="0,1"/>
</div>
<div id="no-clone-exception"><object><embed></embed></object></div>
<div id="names-group">
<span id="name-is-example" name="example"></span>
<span id="name-is-div" name="div"></span>
</div>
<script id="script-no-src"></script>
<script id="script-src" src="data/empty.js"></script>
<div id="id-name-tests">
<a id="tName1ID" name="tName1"><span></span></a>
<a id="tName2ID" name="tName2"><span></span></a>
<div id="tName1"><span id="tName1-span">C</span></div>
</div>
</div>
<div id="tabindex-tests">
<ol id="listWithTabIndex" tabindex="5">
<li id="foodWithNegativeTabIndex" tabindex="-1">Rice</li>
<li id="foodNoTabIndex">Beans</li>
<li>Blinis</li>
<li>Tofu</li>
</ol>
<div id="divWithNoTabIndex">I'm hungry. I should...</div>
<span>...</span><a href="#" id="linkWithNoTabIndex">Eat lots of food</a><span>...</span> |
<span>...</span><a href="#" id="linkWithTabIndex" tabindex="2">Eat a little food</a><span>...</span> |
<span>...</span><a href="#" id="linkWithNegativeTabIndex" tabindex="-1">Eat no food</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithNoTabIndex">Eat a burger</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithTabIndex" tabindex="1">Eat some funyuns</a><span>...</span>
<span>...</span><a id="linkWithNoHrefWithNegativeTabIndex" tabindex="-1">Eat some funyuns</a><span>...</span>
</div>
<div id="liveHandlerOrder">
<span id="liveSpan1"><a href="#" id="liveLink1"></a></span>
<span id="liveSpan2"><a href="#" id="liveLink2"></a></span>
</div>
<div id="siblingTest">
<em id="siblingfirst">1</em>
<em id="siblingnext">2</em>
<em id="siblingthird">
<em id="siblingchild">
<em id="siblinggrandchild">
<em id="siblinggreatgrandchild"></em>
</em>
</em>
</em>
<span id="siblingspan"></span>
</div>
</div>
</dl>
<br id="last"/>
</body>
</html>

9597
public/vendor/sizzle/test/jquery.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,244 @@
/**
* QUnit v1.12.0 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3c510c;
background-color: #fff;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
module("extending", { teardown: moduleTeardown });
test("custom pseudos", function() {
expect( 6 );
Sizzle.selectors.filters.foundation = Sizzle.selectors.filters.root;
deepEqual( Sizzle(":foundation"), [ document.documentElement ], "Copy element filter with new name" );
delete Sizzle.selectors.filters.foundation;
Sizzle.selectors.setFilters.primary = Sizzle.selectors.setFilters.first;
t( "Copy set filter with new name", "div:primary", ["qunit"] );
delete Sizzle.selectors.setFilters.primary;
Sizzle.selectors.filters.aristotlean = Sizzle.selectors.createPseudo(function() {
return function( elem ) {
return !!elem.id;
};
});
t( "Custom element filter", "#foo :aristotlean", [ "sndp", "en", "yahoo", "sap", "anchor2", "simon" ] );
delete Sizzle.selectors.filters.aristotlean;
Sizzle.selectors.filters.endswith = Sizzle.selectors.createPseudo(function( text ) {
return function( elem ) {
return Sizzle.getText( elem ).slice( -text.length ) === text;
};
});
t( "Custom element filter with argument", "a:endswith(ogle)", ["google"] );
delete Sizzle.selectors.filters.endswith;
Sizzle.selectors.setFilters.second = Sizzle.selectors.createPseudo(function() {
return Sizzle.selectors.createPseudo(function( seed, matches ) {
if ( seed[1] ) {
matches[1] = seed[1];
seed[1] = false;
}
});
});
t( "Custom set filter", "#qunit-fixture p:second", ["ap"] );
delete Sizzle.selectors.filters.second;
Sizzle.selectors.setFilters.slice = Sizzle.selectors.createPseudo(function( argument ) {
var bounds = argument.split(":");
return Sizzle.selectors.createPseudo(function( seed, matches ) {
var i = bounds[1];
// Match elements found at the specified indexes
while ( --i >= bounds[0] ) {
if ( seed[i] ) {
matches[i] = seed[i];
seed[i] = false;
}
}
});
});
t( "Custom set filter with argument", "#qunit-fixture p:slice(1:3)", [ "ap", "sndp" ] );
delete Sizzle.selectors.filters.slice;
});
test("backwards-compatible custom pseudos", function() {
expect( 3 );
Sizzle.selectors.filters.icontains = function( elem, i, match ) {
return Sizzle.getText( elem ).toLowerCase().indexOf( (match[3] || "").toLowerCase() ) > -1;
};
t( "Custom element filter with argument", "a:icontains(THIS BLOG ENTRY)", ["simon1"] );
delete Sizzle.selectors.filters.icontains;
Sizzle.selectors.setFilters.podium = function( elements, argument ) {
var count = argument == null || argument === "" ? 3 : +argument;
return elements.slice( 0, count );
};
// Using TAG as the first token here forces this setMatcher into a fail state
// Where the descendent combinator was lost
t( "Custom setFilter", "form#form :PODIUM", ["label-for", "text1", "text2"] );
t( "Custom setFilter with argument", "#form input:Podium(1)", ["text1"] );
delete Sizzle.selectors.setFilters.podium;
});
test("custom attribute getters", function() {
expect( 2 );
var original = Sizzle.selectors.attrHandle.hreflang,
selector = "a:contains('mark')[hreflang='http://diveintomark.org/en']";
Sizzle.selectors.attrHandle.hreflang = function( elem, name ) {
var href = elem.getAttribute("href"),
lang = elem.getAttribute( name );
return lang && ( href + lang );
};
deepEqual( Sizzle(selector, createWithFriesXML()), [], "Custom attrHandle (preferred document)" );
t( "Custom attrHandle (preferred document)", selector, ["mark"] );
Sizzle.selectors.attrHandle.hreflang = original;
});

1138
public/vendor/sizzle/test/unit/selector.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,169 @@
module("utilities", { teardown: moduleTeardown });
function testAttr( doc ) {
expect( 9 );
var el;
if ( doc ) {
// XML
el = doc.createElement( "input" );
el.setAttribute( "type", "checkbox" );
} else {
// Set checked on creation by creating with a fragment
// See http://jsfiddle.net/8sVgA/1/show/light in oldIE
el = jQuery( "<input type='checkbox' checked='checked' />" )[0];
}
// Set it again for good measure
el.setAttribute( "checked", "checked" );
el.setAttribute( "id", "id" );
el.setAttribute( "value", "on" );
strictEqual( Sizzle.attr( el, "nonexistent" ), null, "nonexistent" );
strictEqual( Sizzle.attr( el, "id" ), "id", "existent" );
strictEqual( Sizzle.attr( el, "value" ), "on", "value" );
strictEqual( Sizzle.attr( el, "checked" ), "checked", "boolean" );
strictEqual( Sizzle.attr( el, "href" ), null, "interpolation risk" );
strictEqual( Sizzle.attr( el, "constructor" ), null,
"Object.prototype property \"constructor\" (negative)" );
strictEqual( Sizzle.attr( el, "watch" ), null,
"Gecko Object.prototype property \"watch\" (negative)" );
el.setAttribute( "constructor", "foo" );
el.setAttribute( "watch", "bar" );
strictEqual( Sizzle.attr( el, "constructor" ), "foo",
"Object.prototype property \"constructor\"" );
strictEqual( Sizzle.attr( el, "watch" ), "bar",
"Gecko Object.prototype property \"watch\"" );
}
test("Sizzle.attr (HTML)", function() {
testAttr();
});
test("Sizzle.attr (XML)", function() {
testAttr( jQuery.parseXML("<root/>") );
});
test("Sizzle.contains", function() {
expect( 16 );
var container = document.getElementById("nonnodes"),
element = container.firstChild,
text = element.nextSibling,
nonContained = container.nextSibling,
detached = document.createElement("a");
ok( element && element.nodeType === 1, "preliminary: found element" );
ok( text && text.nodeType === 3, "preliminary: found text" );
ok( nonContained, "preliminary: found non-descendant" );
ok( Sizzle.contains(container, element), "child" );
ok( Sizzle.contains(container.parentNode, element), "grandchild" );
ok( Sizzle.contains(container, text), "text child" );
ok( Sizzle.contains(container.parentNode, text), "text grandchild" );
ok( !Sizzle.contains(container, container), "self" );
ok( !Sizzle.contains(element, container), "parent" );
ok( !Sizzle.contains(container, nonContained), "non-descendant" );
ok( !Sizzle.contains(container, document), "document" );
ok( !Sizzle.contains(container, document.documentElement), "documentElement (negative)" );
ok( !Sizzle.contains(container, null), "Passing null does not throw an error" );
ok( Sizzle.contains(document, document.documentElement), "documentElement (positive)" );
ok( Sizzle.contains(document, element), "document container (positive)" );
ok( !Sizzle.contains(document, detached), "document container (negative)" );
});
if ( jQuery("<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='1' width='1'><g/></svg>")[0].firstChild ) {
test("Sizzle.contains in SVG (jQuery #10832)", function() {
expect( 4 );
var svg = jQuery(
"<svg xmlns='http://www.w3.org/2000/svg' version='1.1' height='1' width='1'>" +
"<g><circle cx='1' cy='1' r='1' /></g>" +
"</svg>"
).appendTo("#qunit-fixture")[0];
ok( Sizzle.contains( svg, svg.firstChild ), "root child" );
ok( Sizzle.contains( svg.firstChild, svg.firstChild.firstChild ), "element child" );
ok( Sizzle.contains( svg, svg.firstChild.firstChild ), "root granchild" );
ok( !Sizzle.contains( svg.firstChild.firstChild, svg.firstChild ), "parent (negative)" );
});
}
test("Sizzle.uniqueSort", function() {
expect( 14 );
function Arrayish( arr ) {
var i = this.length = arr.length;
while ( i-- ) {
this[ i ] = arr[ i ];
}
}
Arrayish.prototype = {
slice: [].slice,
sort: [].sort,
splice: [].splice
};
var i, tests,
detached = [],
body = document.body,
fixture = document.getElementById("qunit-fixture"),
detached1 = document.createElement("p"),
detached2 = document.createElement("ul"),
detachedChild = detached1.appendChild( document.createElement("a") ),
detachedGrandchild = detachedChild.appendChild( document.createElement("b") );
for ( i = 0; i < 12; i++ ) {
detached.push( document.createElement("li") );
detached[i].id = "detached" + i;
detached2.appendChild( document.createElement("li") ).id = "detachedChild" + i;
}
tests = {
"Empty": {
input: [],
expected: []
},
"Single-element": {
input: [ fixture ],
expected: [ fixture ]
},
"No duplicates": {
input: [ fixture, body ],
expected: [ body, fixture ]
},
"Duplicates": {
input: [ body, fixture, fixture, body ],
expected: [ body, fixture ]
},
"Detached": {
input: detached.slice( 0 ),
expected: detached.slice( 0 )
},
"Detached children": {
input: [
detached2.childNodes[0],
detached2.childNodes[1],
detached2.childNodes[2],
detached2.childNodes[3]
],
expected: [
detached2.childNodes[0],
detached2.childNodes[1],
detached2.childNodes[2],
detached2.childNodes[3]
]
},
"Attached/detached mixture": {
input: [ detached1, fixture, detached2, document, detachedChild, body, detachedGrandchild ],
expected: [ document, body, fixture ],
length: 3
}
};
jQuery.each( tests, function( label, test ) {
var length = test.length || test.input.length;
deepEqual( Sizzle.uniqueSort( test.input ).slice( 0, length ), test.expected, label + " (array)" );
deepEqual( Sizzle.uniqueSort( new Arrayish(test.input) ).slice( 0, length ), test.expected, label + " (quasi-array)" );
});
});
testIframeWithCallback( "Sizzle.uniqueSort works cross-window (jQuery #14381)", "mixed_sort.html", deepEqual );