- t('You have no contacts in your list.') ?>
+ t('You have no contacts in your addressbook.') ?>
diff --git a/apps/external/appinfo/info.xml b/apps/external/appinfo/info.xml
index 05f5709916..83130f17e6 100644
--- a/apps/external/appinfo/info.xml
+++ b/apps/external/appinfo/info.xml
@@ -3,7 +3,6 @@
external
External
Show external Application in the ownCloud menu
-
1.0
AGPL
Frank Karlitschek
2
diff --git a/apps/external/appinfo/version b/apps/external/appinfo/version
new file mode 100644
index 0000000000..9f8e9b69a3
--- /dev/null
+++ b/apps/external/appinfo/version
@@ -0,0 +1 @@
+1.0
\ No newline at end of file
diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml
index 236b5a64b0..9872187f9b 100644
--- a/apps/files_archive/appinfo/info.xml
+++ b/apps/files_archive/appinfo/info.xml
@@ -3,11 +3,11 @@
files_archive
Archive support
Transparent opening of archives
-
0.1
AGPL
Robin Appelman
3
+
diff --git a/apps/files_archive/appinfo/version b/apps/files_archive/appinfo/version
new file mode 100644
index 0000000000..ceab6e11ec
--- /dev/null
+++ b/apps/files_archive/appinfo/version
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/apps/files_archive/lib/tar.php b/apps/files_archive/lib/tar.php
index a5d5400478..1eed11a762 100644
--- a/apps/files_archive/lib/tar.php
+++ b/apps/files_archive/lib/tar.php
@@ -30,8 +30,8 @@ class OC_Archive_TAR extends OC_Archive{
*/
static public function getTarType($file){
if(strpos($file,'.')){
- $extention=substr($file,strrpos($file,'.'));
- switch($extention){
+ $extension=substr($file,strrpos($file,'.'));
+ switch($extension){
case 'gz':
case 'tgz':
return self::GZIP;
diff --git a/apps/files_archive/tests/tar.php b/apps/files_archive/tests/tar.php
index 193a65b550..aa46455e65 100644
--- a/apps/files_archive/tests/tar.php
+++ b/apps/files_archive/tests/tar.php
@@ -8,13 +8,17 @@
require_once('archive.php');
-class Test_Archive_TAR extends Test_Archive{
- protected function getExisting(){
- $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
- return new OC_Archive_TAR($dir.'/data.tar.gz');
- }
+if(is_dir(OC::$SERVERROOT.'/apps/files_archive/tests/data')){
+ class Test_Archive_TAR extends Test_Archive{
+ protected function getExisting(){
+ $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+ return new OC_Archive_TAR($dir.'/data.tar.gz');
+ }
- protected function getNew(){
- return new OC_Archive_TAR(OC_Helper::tmpFile('.tar.gz'));
+ protected function getNew(){
+ return new OC_Archive_TAR(OC_Helper::tmpFile('.tar.gz'));
+ }
}
+}else{
+ abstract class Test_Archive_TAR extends Test_Archive{}
}
diff --git a/apps/files_archive/tests/zip.php b/apps/files_archive/tests/zip.php
index 3ff713eda7..18a2997c1a 100644
--- a/apps/files_archive/tests/zip.php
+++ b/apps/files_archive/tests/zip.php
@@ -8,13 +8,17 @@
require_once('archive.php');
-class Test_Archive_ZIP extends Test_Archive{
- protected function getExisting(){
- $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
- return new OC_Archive_ZIP($dir.'/data.zip');
- }
+if(is_dir(OC::$SERVERROOT.'/apps/files_archive/tests/data')){
+ class Test_Archive_ZIP extends Test_Archive{
+ protected function getExisting(){
+ $dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+ return new OC_Archive_ZIP($dir.'/data.zip');
+ }
- protected function getNew(){
- return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip'));
+ protected function getNew(){
+ return new OC_Archive_ZIP(OC_Helper::tmpFile('.zip'));
+ }
}
+}else{
+ abstract class Test_Archive_ZIP extends Test_Archive{}
}
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
index 691b265bf6..c2e1aa9604 100644
--- a/apps/files_encryption/appinfo/info.xml
+++ b/apps/files_encryption/appinfo/info.xml
@@ -3,7 +3,6 @@
files_encryption
Encryption
Server side encryption of files
-
0.1
AGPL
Robin Appelman
3
diff --git a/apps/files_encryption/appinfo/version b/apps/files_encryption/appinfo/version
new file mode 100644
index 0000000000..ceab6e11ec
--- /dev/null
+++ b/apps/files_encryption/appinfo/version
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php
index c1c26d7754..c68df06f9f 100644
--- a/apps/files_encryption/lib/proxy.php
+++ b/apps/files_encryption/lib/proxy.php
@@ -41,8 +41,8 @@ class OC_FileProxy_Encryption extends OC_FileProxy{
if(self::isEncrypted($path)){
return true;
}
- $extention=substr($path,strrpos($path,'.')+1);
- if(array_search($extention,self::$blackList)===false){
+ $extension=substr($path,strrpos($path,'.')+1);
+ if(array_search($extension,self::$blackList)===false){
return true;
}
}
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
index fb58297ff1..1918925389 100644
--- a/apps/files_external/appinfo/info.xml
+++ b/apps/files_external/appinfo/info.xml
@@ -3,7 +3,6 @@
files_external
External storage support
Mount external storage sources
-
0.1
AGPL
Robin Appelman
3
diff --git a/apps/files_external/appinfo/version b/apps/files_external/appinfo/version
new file mode 100644
index 0000000000..ceab6e11ec
--- /dev/null
+++ b/apps/files_external/appinfo/version
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/apps/files_imageviewer/appinfo/info.xml b/apps/files_imageviewer/appinfo/info.xml
index 00b55c254d..dbc78ffba0 100644
--- a/apps/files_imageviewer/appinfo/info.xml
+++ b/apps/files_imageviewer/appinfo/info.xml
@@ -3,7 +3,6 @@
files_imageviewer
Image Viewer
Simple image viewer for owncloud
-
1.0
AGPL
Robin Appelman
2
diff --git a/apps/files_imageviewer/appinfo/version b/apps/files_imageviewer/appinfo/version
new file mode 100644
index 0000000000..9f8e9b69a3
--- /dev/null
+++ b/apps/files_imageviewer/appinfo/version
@@ -0,0 +1 @@
+1.0
\ No newline at end of file
diff --git a/apps/files_imageviewer/js/lightbox.js b/apps/files_imageviewer/js/lightbox.js
index 94743aa85e..6c97466e6d 100644
--- a/apps/files_imageviewer/js/lightbox.js
+++ b/apps/files_imageviewer/js/lightbox.js
@@ -18,6 +18,9 @@ $(document).ready(function() {
});
function viewImage(dir, file) {
+ if(file.indexOf('.psd')){//can't view those
+ return;
+ }
var location=OC.filePath('files','ajax','download.php')+'?files='+file+'&dir='+dir;
$.fancybox({
"href": location,
diff --git a/apps/files_pdfviewer/appinfo/info.xml b/apps/files_pdfviewer/appinfo/info.xml
index f133f1900d..0e81729a8b 100755
--- a/apps/files_pdfviewer/appinfo/info.xml
+++ b/apps/files_pdfviewer/appinfo/info.xml
@@ -3,7 +3,6 @@
files_pdfviewer
PDF Viewer
Inline PDF viewer (pdfjs-based)
-
0.1
GPL
Joan Creus
2
diff --git a/apps/files_pdfviewer/appinfo/version b/apps/files_pdfviewer/appinfo/version
new file mode 100644
index 0000000000..ceab6e11ec
--- /dev/null
+++ b/apps/files_pdfviewer/appinfo/version
@@ -0,0 +1 @@
+0.1
\ No newline at end of file
diff --git a/apps/files_pdfviewer/js/pdfjs/build/pdf.js b/apps/files_pdfviewer/js/pdfjs/build/pdf.js
old mode 100755
new mode 100644
index 3447358d3b..a19a9b75fe
--- a/apps/files_pdfviewer/js/pdfjs/build/pdf.js
+++ b/apps/files_pdfviewer/js/pdfjs/build/pdf.js
@@ -7,7 +7,7 @@ var PDFJS = {};
// Use strict in our context only - users might not want it
'use strict';
- PDFJS.build = 'PDFJSSCRIPT_BUNDLE_VER';
+ PDFJS.build = 'd823592';
// Files are inserted below - see Makefile
/* PDFJSSCRIPT_INCLUDE_ALL */
@@ -18,6 +18,8 @@ var PDFJS = {};
var globalScope = (typeof window === 'undefined') ? this : window;
+var isWorker = (typeof window == 'undefined');
+
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
var verbosity = WARNINGS;
@@ -44,7 +46,9 @@ function getPdf(arg, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', params.url);
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
- xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
+ var protocol = params.url.indexOf(':') < 0 ? window.location.protocol :
+ params.url.substring(0, params.url.indexOf(':') + 1);
+ xhr.expected = (protocol === 'http:' || protocol === 'https:') ? 200 : 0;
if ('progress' in params)
xhr.onprogress = params.progress || undefined;
@@ -52,41 +56,43 @@ function getPdf(arg, callback) {
if ('error' in params)
xhr.onerror = params.error || undefined;
- xhr.onreadystatechange = function getPdfOnreadystatechange() {
- if (xhr.readyState === 4 && xhr.status === xhr.expected) {
- var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
- xhr.responseArrayBuffer || xhr.response);
- callback(data);
+ xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === xhr.expected) {
+ var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
+ xhr.responseArrayBuffer || xhr.response);
+ callback(data);
+ } else if (params.error) {
+ params.error(e);
+ }
}
};
xhr.send(null);
}
globalScope.PDFJS.getPdf = getPdf;
+globalScope.PDFJS.pdfBug = false;
-var Page = (function pagePage() {
- function constructor(xref, pageNumber, pageDict, ref) {
+var Page = (function PageClosure() {
+ function Page(xref, pageNumber, pageDict, ref) {
this.pageNumber = pageNumber;
this.pageDict = pageDict;
- this.stats = {
- create: Date.now(),
- compile: 0.0,
- fonts: 0.0,
- images: 0.0,
- render: 0.0
- };
+ this.stats = new StatTimer();
+ this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.xref = xref;
this.ref = ref;
+
+ this.displayReadyPromise = null;
}
- constructor.prototype = {
- getPageProp: function pageGetPageProp(key) {
- return this.xref.fetchIfRef(this.pageDict.get(key));
+ Page.prototype = {
+ getPageProp: function Page_getPageProp(key) {
+ return this.pageDict.get(key);
},
- inheritPageProp: function pageInheritPageProp(key) {
+ inheritPageProp: function Page_inheritPageProp(key) {
var dict = this.pageDict;
var obj = dict.get(key);
while (obj === undefined) {
- dict = this.xref.fetchIfRef(dict.get('Parent'));
+ dict = dict.get('Parent');
if (!dict)
break;
obj = dict.get(key);
@@ -107,23 +113,35 @@ var Page = (function pagePage() {
return shadow(this, 'mediaBox', obj);
},
get view() {
- var obj = this.inheritPageProp('CropBox');
+ var cropBox = this.inheritPageProp('CropBox');
var view = {
x: 0,
y: 0,
width: this.width,
height: this.height
};
- if (isArray(obj) && obj.length == 4) {
- var tl = this.rotatePoint(obj[0], obj[1]);
- var br = this.rotatePoint(obj[2], obj[3]);
- view.x = Math.min(tl.x, br.x);
- view.y = Math.min(tl.y, br.y);
- view.width = Math.abs(tl.x - br.x);
- view.height = Math.abs(tl.y - br.y);
- }
+ if (!isArray(cropBox) || cropBox.length !== 4)
+ return shadow(this, 'view', view);
- return shadow(this, 'cropBox', view);
+ var mediaBox = this.mediaBox;
+ var offsetX = mediaBox[0], offsetY = mediaBox[1];
+
+ // From the spec, 6th ed., p.963:
+ // "The crop, bleed, trim, and art boxes should not ordinarily
+ // extend beyond the boundaries of the media box. If they do, they are
+ // effectively reduced to their intersection with the media box."
+ cropBox = Util.intersect(cropBox, mediaBox);
+ if (!cropBox)
+ return shadow(this, 'view', view);
+
+ var tl = this.rotatePoint(cropBox[0] - offsetX, cropBox[1] - offsetY);
+ var br = this.rotatePoint(cropBox[2] - offsetX, cropBox[3] - offsetY);
+ view.x = Math.min(tl.x, br.x);
+ view.y = Math.min(tl.y, br.y);
+ view.width = Math.abs(tl.x - br.x);
+ view.height = Math.abs(tl.y - br.y);
+
+ return shadow(this, 'view', view);
},
get annotations() {
return shadow(this, 'annotations', this.inheritPageProp('Annots'));
@@ -165,77 +183,80 @@ var Page = (function pagePage() {
return shadow(this, 'rotate', rotate);
},
- startRenderingFromIRQueue: function pageStartRenderingFromIRQueue(
- IRQueue, fonts) {
+ startRenderingFromOperatorList:
+ function Page_startRenderingFromOperatorList(operatorList, fonts) {
var self = this;
- this.IRQueue = IRQueue;
- var gfx = new CanvasGraphics(this.ctx, this.objs);
+ this.operatorList = operatorList;
var displayContinuation = function pageDisplayContinuation() {
// Always defer call to display() to work around bug in
// Firefox error reporting from XHR callbacks.
setTimeout(function pageSetTimeout() {
- try {
- self.display(gfx, self.callback);
- } catch (e) {
- if (self.callback) self.callback(e.toString());
- throw e;
- }
+ self.displayReadyPromise.resolve();
});
};
this.ensureFonts(fonts,
- function pageStartRenderingFromIRQueueEnsureFonts() {
- displayContinuation();
- });
+ function pageStartRenderingFromOperatorListEnsureFonts() {
+ displayContinuation();
+ }
+ );
},
- getIRQueue: function pageGetIRQueue(handler, dependency) {
- if (this.IRQueue) {
+ getOperatorList: function Page_getOperatorList(handler, dependency) {
+ if (this.operatorList) {
// content was compiled
- return this.IRQueue;
+ return this.operatorList;
}
+ this.stats.time('Build IR Queue');
+
var xref = this.xref;
- var content = xref.fetchIfRef(this.content);
- var resources = xref.fetchIfRef(this.resources);
+ var content = this.content;
+ var resources = this.resources;
if (isArray(content)) {
// fetching items
var i, n = content.length;
for (i = 0; i < n; ++i)
content[i] = xref.fetchIfRef(content[i]);
content = new StreamsSequenceStream(content);
+ } else if (!content) {
+ // replacing non-existent page content with empty one
+ content = new Stream(new Uint8Array(0));
}
var pe = this.pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_');
- var IRQueue = {};
- return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
- dependency));
+
+ this.operatorList = pe.getOperatorList(content, resources, dependency);
+ this.stats.timeEnd('Build IR Queue');
+ return this.operatorList;
},
- ensureFonts: function pageEnsureFonts(fonts, callback) {
+ ensureFonts: function Page_ensureFonts(fonts, callback) {
+ this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
- var fontObjs = FontLoader.bind(
+ FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
- this.stats.fonts = Date.now();
+ this.stats.timeEnd('Font Loading');
callback.call(this);
- }.bind(this),
- this.objs
+ }.bind(this)
);
},
- display: function pageDisplay(gfx, callback) {
+ display: function Page_display(gfx, callback) {
+ var stats = this.stats;
+ stats.time('Rendering');
var xref = this.xref;
- var resources = xref.fetchIfRef(this.resources);
- var mediaBox = xref.fetchIfRef(this.mediaBox);
+ var resources = this.resources;
+ var mediaBox = this.mediaBox;
assertWellFormed(isDict(resources), 'invalid page resources');
gfx.xref = xref;
@@ -246,20 +267,29 @@ var Page = (function pagePage() {
rotate: this.rotate });
var startIdx = 0;
- var length = this.IRQueue.fnArray.length;
- var IRQueue = this.IRQueue;
+ var length = this.operatorList.fnArray.length;
+ var operatorList = this.operatorList;
+ var stepper = null;
+ if (PDFJS.pdfBug && StepperManager.enabled) {
+ stepper = StepperManager.create(this.pageNumber);
+ stepper.init(operatorList);
+ stepper.nextBreakPoint = stepper.getNextBreakPoint();
+ }
var self = this;
function next() {
- startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
+ startIdx =
+ gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) {
- self.stats.render = Date.now();
+ gfx.endDrawing();
+ stats.timeEnd('Rendering');
+ stats.timeEnd('Overall');
if (callback) callback();
}
}
next();
},
- rotatePoint: function pageRotatePoint(x, y, reverse) {
+ rotatePoint: function Page_rotatePoint(x, y, reverse) {
var rotate = reverse ? (360 - this.rotate) : this.rotate;
switch (rotate) {
case 180:
@@ -274,58 +304,183 @@ var Page = (function pagePage() {
return {x: x, y: this.height - y};
}
},
- getLinks: function pageGetLinks() {
- var xref = this.xref;
- var annotations = xref.fetchIfRef(this.annotations) || [];
- var i, n = annotations.length;
+ getLinks: function Page_getLinks() {
var links = [];
+ var annotations = pageGetAnnotations();
+ var i, n = annotations.length;
for (i = 0; i < n; ++i) {
- var annotation = xref.fetch(annotations[i]);
+ if (annotations[i].type != 'Link')
+ continue;
+ links.push(annotations[i]);
+ }
+ return links;
+ },
+ getAnnotations: function Page_getAnnotations() {
+ var xref = this.xref;
+ function getInheritableProperty(annotation, name) {
+ var item = annotation;
+ while (item && !item.has(name)) {
+ item = item.get('Parent');
+ }
+ if (!item)
+ return null;
+ return item.get(name);
+ }
+ function isValidUrl(url) {
+ if (!url)
+ return false;
+ var colon = url.indexOf(':');
+ if (colon < 0)
+ return false;
+ var protocol = url.substr(0, colon);
+ switch (protocol) {
+ case 'http':
+ case 'https':
+ case 'ftp':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ var annotations = this.annotations || [];
+ var i, n = annotations.length;
+ var items = [];
+ for (i = 0; i < n; ++i) {
+ var annotationRef = annotations[i];
+ var annotation = xref.fetch(annotationRef);
if (!isDict(annotation))
continue;
var subtype = annotation.get('Subtype');
- if (!isName(subtype) || subtype.name != 'Link')
+ if (!isName(subtype))
continue;
var rect = annotation.get('Rect');
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
- var link = {};
- link.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
- link.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
- link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
- link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
- var a = this.xref.fetchIfRef(annotation.get('A'));
- if (a) {
- switch (a.get('S').name) {
- case 'URI':
- link.url = a.get('URI');
+ var item = {};
+ item.type = subtype.name;
+ item.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
+ item.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
+ item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
+ item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
+ switch (subtype.name) {
+ case 'Link':
+ var a = annotation.get('A');
+ if (a) {
+ switch (a.get('S').name) {
+ case 'URI':
+ var url = a.get('URI');
+ // TODO: pdf spec mentions urls can be relative to a Base
+ // entry in the dictionary.
+ if (!isValidUrl(url))
+ url = '';
+ item.url = url;
+ break;
+ case 'GoTo':
+ item.dest = a.get('D');
+ break;
+ default:
+ TODO('other link types');
+ }
+ } else if (annotation.has('Dest')) {
+ // simple destination link
+ var dest = annotation.get('Dest');
+ item.dest = isName(dest) ? dest.name : dest;
+ }
+ break;
+ case 'Widget':
+ var fieldType = getInheritableProperty(annotation, 'FT');
+ if (!isName(fieldType))
break;
- case 'GoTo':
- link.dest = a.get('D');
- break;
- default:
- TODO('other link types');
- }
- } else if (annotation.has('Dest')) {
- // simple destination link
- var dest = annotation.get('Dest');
- link.dest = isName(dest) ? dest.name : dest;
+ item.fieldType = fieldType.name;
+ // Building the full field name by collecting the field and
+ // its ancestors 'T' properties and joining them using '.'.
+ var fieldName = [];
+ var namedItem = annotation, ref = annotationRef;
+ while (namedItem) {
+ var parent = namedItem.get('Parent');
+ var parentRef = namedItem.getRaw('Parent');
+ var name = namedItem.get('T');
+ if (name) {
+ fieldName.unshift(stringToPDFString(name));
+ } else {
+ // The field name is absent, that means more than one field
+ // with the same name may exist. Replacing the empty name
+ // with the '`' plus index in the parent's 'Kids' array.
+ // This is not in the PDF spec but necessary to id the
+ // the input controls.
+ var kids = parent.get('Kids');
+ var j, jj;
+ for (j = 0, jj = kids.length; j < jj; j++) {
+ var kidRef = kids[j];
+ if (kidRef.num == ref.num && kidRef.gen == ref.gen)
+ break;
+ }
+ fieldName.unshift('`' + j);
+ }
+ namedItem = parent;
+ ref = parentRef;
+ }
+ item.fullName = fieldName.join('.');
+ var alternativeText = stringToPDFString(annotation.get('TU') || '');
+ item.alternativeText = alternativeText;
+ var da = getInheritableProperty(annotation, 'DA') || '';
+ var m = /([\d\.]+)\sTf/.exec(da);
+ if (m)
+ item.fontSize = parseFloat(m[1]);
+ item.textAlignment = getInheritableProperty(annotation, 'Q');
+ item.flags = getInheritableProperty(annotation, 'Ff') || 0;
+ break;
+ case 'Text':
+ var content = annotation.get('Contents');
+ var title = annotation.get('T');
+ item.content = stringToPDFString(content || '');
+ item.title = stringToPDFString(title || '');
+ item.name = annotation.get('Name').name;
+ break;
+ default:
+ TODO('unimplemented annotation type: ' + subtype.name);
+ break;
}
- links.push(link);
+ items.push(item);
}
- return links;
+ return items;
},
- startRendering: function pageStartRendering(ctx, callback) {
- this.ctx = ctx;
- this.callback = callback;
+ startRendering: function Page_startRendering(ctx, callback, textLayer) {
+ var stats = this.stats;
+ stats.time('Overall');
+ // If there is no displayReadyPromise yet, then the operatorList was never
+ // requested before. Make the request and create the promise.
+ if (!this.displayReadyPromise) {
+ this.pdf.startRendering(this);
+ this.displayReadyPromise = new Promise();
+ }
- this.startRenderingTime = Date.now();
- this.pdf.startRendering(this);
+ // Once the operatorList and fonts are loaded, do the actual rendering.
+ this.displayReadyPromise.then(
+ function pageDisplayReadyPromise() {
+ var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
+ try {
+ this.display(gfx, callback);
+ } catch (e) {
+ if (callback)
+ callback(e);
+ else
+ error(e);
+ }
+ }.bind(this),
+ function pageDisplayReadPromiseError(reason) {
+ if (callback)
+ callback(reason);
+ else
+ error(reason);
+ }
+ );
}
};
- return constructor;
+ return Page;
})();
/**
@@ -334,12 +489,9 @@ var Page = (function pagePage() {
* Right now there exists one PDFDocModel on the main thread + one object
* for each worker. If there is no worker support enabled, there are two
* `PDFDocModel` objects on the main thread created.
- * TODO: Refactor the internal object structure, such that there is no
- * need for the `PDFDocModel` anymore and there is only one object on the
- * main thread and not one entire copy on each worker instance.
*/
-var PDFDocModel = (function pdfDoc() {
- function constructor(arg, callback) {
+var PDFDocModel = (function PDFDocModelClosure() {
+ function PDFDocModel(arg, callback) {
if (isStream(arg))
init.call(this, arg);
else if (isArrayBuffer(arg))
@@ -352,6 +504,7 @@ var PDFDocModel = (function pdfDoc() {
assertWellFormed(stream.length > 0, 'stream must have data');
this.stream = stream;
this.setup();
+ this.acroForm = this.catalog.catDict.get('AcroForm');
}
function find(stream, needle, limit, backwards) {
@@ -370,7 +523,7 @@ var PDFDocModel = (function pdfDoc() {
return true; /* found */
}
- constructor.prototype = {
+ PDFDocModel.prototype = {
get linearization() {
var length = this.stream.length;
var linearization = false;
@@ -392,12 +545,17 @@ var PDFDocModel = (function pdfDoc() {
if (find(stream, 'endobj', 1024))
startXRef = stream.pos + 6;
} else {
- // Find startxref at the end of the file.
- var start = stream.end - 1024;
- if (start < 0)
- start = 0;
- stream.pos = start;
- if (find(stream, 'startxref', 1024, true)) {
+ // Find startxref by jumping backward from the end of the file.
+ var step = 1024;
+ var found = false, pos = stream.end;
+ while (!found && pos > 0) {
+ pos -= step - 'startxref'.length;
+ if (pos < 0)
+ pos = 0;
+ stream.pos = pos;
+ found = find(stream, 'startxref', step, true);
+ }
+ if (found) {
stream.skip(9);
var ch;
do {
@@ -426,7 +584,7 @@ var PDFDocModel = (function pdfDoc() {
},
// Find the header, remove leading garbage and setup the stream
// starting from the header.
- checkHeader: function pdfDocCheckHeader() {
+ checkHeader: function PDFDocModel_checkHeader() {
var stream = this.stream;
stream.reset();
if (find(stream, '%PDF-', 1024)) {
@@ -436,12 +594,13 @@ var PDFDocModel = (function pdfDoc() {
}
// May not be a PDF file, continue anyway.
},
- setup: function pdfDocSetup(ownerPassword, userPassword) {
+ setup: function PDFDocModel_setup(ownerPassword, userPassword) {
this.checkHeader();
- this.xref = new XRef(this.stream,
- this.startXRef,
- this.mainXRefEntriesOffset);
- this.catalog = new Catalog(this.xref);
+ var xref = new XRef(this.stream,
+ this.startXRef,
+ this.mainXRefEntriesOffset);
+ this.xref = xref;
+ this.catalog = new Catalog(xref);
},
get numPages() {
var linearization = this.linearization;
@@ -449,16 +608,51 @@ var PDFDocModel = (function pdfDoc() {
// shadow the prototype getter
return shadow(this, 'numPages', num);
},
- getPage: function pdfDocGetPage(n) {
+ getDocumentInfo: function PDFDocModel_getDocumentInfo() {
+ var info;
+ if (this.xref.trailer.has('Info')) {
+ var infoDict = this.xref.trailer.get('Info');
+
+ info = {};
+ infoDict.forEach(function(key, value) {
+ info[key] = typeof value !== 'string' ? value :
+ stringToPDFString(value);
+ });
+ }
+
+ return shadow(this, 'getDocumentInfo', info);
+ },
+ getFingerprint: function PDFDocModel_getFingerprint() {
+ var xref = this.xref, fileID;
+ if (xref.trailer.has('ID')) {
+ fileID = '';
+ var id = xref.trailer.get('ID')[0];
+ id.split('').forEach(function(el) {
+ fileID += Number(el.charCodeAt(0)).toString(16);
+ });
+ } else {
+ // If we got no fileID, then we generate one,
+ // from the first 100 bytes of PDF
+ var data = this.stream.bytes.subarray(0, 100);
+ var hash = calculateMD5(data, 0, data.length);
+ fileID = '';
+ for (var i = 0, length = hash.length; i < length; i++) {
+ fileID += Number(hash[i]).toString(16);
+ }
+ }
+
+ return shadow(this, 'getFingerprint', fileID);
+ },
+ getPage: function PDFDocModel_getPage(n) {
return this.catalog.getPage(n);
}
};
- return constructor;
+ return PDFDocModel;
})();
-var PDFDoc = (function pdfDoc() {
- function constructor(arg, callback) {
+var PDFDoc = (function PDFDocClosure() {
+ function PDFDoc(arg, callback) {
var stream = null;
var data = null;
@@ -474,9 +668,10 @@ var PDFDoc = (function pdfDoc() {
this.data = data;
this.stream = stream;
- this.pdf = new PDFDocModel(stream);
-
- this.catalog = this.pdf.catalog;
+ this.pdfModel = new PDFDocModel(stream);
+ this.fingerprint = this.pdfModel.getFingerprint();
+ this.info = this.pdfModel.getDocumentInfo();
+ this.catalog = this.pdfModel.catalog;
this.objs = new PDFObjects();
this.pageCache = [];
@@ -491,49 +686,59 @@ var PDFDoc = (function pdfDoc() {
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
var workerSrc = PDFJS.workerSrc;
if (typeof workerSrc === 'undefined') {
- throw 'No PDFJS.workerSrc specified';
+ error('No PDFJS.workerSrc specified');
}
- var worker;
try {
- worker = new Worker(workerSrc);
- } catch (e) {
- // Some versions of FF can't create a worker on localhost, see:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
- globalScope.PDFJS.disableWorker = true;
- this.setupFakeWorker();
- return;
- }
-
- var messageHandler = new MessageHandler('main', worker);
-
- // Tell the worker the file it was created from.
- messageHandler.send('workerSrc', workerSrc);
-
- messageHandler.on('test', function pdfDocTest(supportTypedArray) {
- if (supportTypedArray) {
- this.worker = worker;
- this.setupMessageHandler(messageHandler);
+ var worker;
+ if (PDFJS.isFirefoxExtension) {
+ // The firefox extension can't load the worker from the resource://
+ // url so we have to inline the script and then use the blob loader.
+ var bb = new MozBlobBuilder();
+ bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent);
+ var blobUrl = window.URL.createObjectURL(bb.getBlob());
+ worker = new Worker(blobUrl);
} else {
- this.setupFakeWorker();
+ // Some versions of FF can't create a worker on localhost, see:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=683280
+ worker = new Worker(workerSrc);
}
- }.bind(this));
- var testObj = new Uint8Array(1);
- messageHandler.send('test', testObj);
- } else {
- this.setupFakeWorker();
+ var messageHandler = new MessageHandler('main', worker);
+
+ messageHandler.on('test', function pdfDocTest(supportTypedArray) {
+ if (supportTypedArray) {
+ this.worker = worker;
+ this.setupMessageHandler(messageHandler);
+ } else {
+ globalScope.PDFJS.disableWorker = true;
+ this.setupFakeWorker();
+ }
+ }.bind(this));
+
+ var testObj = new Uint8Array(1);
+ // Some versions of Opera throw a DATA_CLONE_ERR on
+ // serializing the typed array.
+ messageHandler.send('test', testObj);
+ return;
+ } catch (e) {
+ warn('The worker has been disabled.');
+ }
}
+ // Either workers are disabled, not supported or have thrown an exception.
+ // Thus, we fallback to a faked worker.
+ globalScope.PDFJS.disableWorker = true;
+ this.setupFakeWorker();
}
- constructor.prototype = {
- setupFakeWorker: function() {
+ PDFDoc.prototype = {
+ setupFakeWorker: function PDFDoc_setupFakeWorker() {
// If we don't use a worker, just post/sendMessage to the main thread.
var fakeWorker = {
- postMessage: function pdfDocPostMessage(obj) {
+ postMessage: function PDFDoc_postMessage(obj) {
fakeWorker.onmessage({data: obj});
},
- terminate: function pdfDocTerminate() {}
+ terminate: function PDFDoc_terminate() {}
};
var messageHandler = new MessageHandler('main', fakeWorker);
@@ -545,7 +750,7 @@ var PDFDoc = (function pdfDoc() {
},
- setupMessageHandler: function(messageHandler) {
+ setupMessageHandler: function PDFDoc_setupMessageHandler(messageHandler) {
this.messageHandler = messageHandler;
messageHandler.on('page', function pdfDocPage(data) {
@@ -553,7 +758,8 @@ var PDFDoc = (function pdfDoc() {
var page = this.pageCache[pageNum];
var depFonts = data.depFonts;
- page.startRenderingFromIRQueue(data.IRQueue, depFonts);
+ page.stats.timeEnd('Page Request');
+ page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this);
messageHandler.on('obj', function pdfDocObj(data) {
@@ -562,8 +768,12 @@ var PDFDoc = (function pdfDoc() {
switch (type) {
case 'JpegStream':
- var IR = data[2];
- new JpegImageLoader(id, IR, this.objs);
+ var imageData = data[2];
+ loadJpegStream(id, imageData, this.objs);
+ break;
+ case 'Image':
+ var imageData = data[2];
+ this.objs.resolve(id, imageData);
break;
case 'Font':
var name = data[2];
@@ -571,46 +781,63 @@ var PDFDoc = (function pdfDoc() {
var properties = data[4];
if (file) {
+ // Rewrap the ArrayBuffer in a stream.
var fontFileDict = new Dict();
- fontFileDict.map = file.dict.map;
-
- var fontFile = new Stream(file.bytes, file.start,
- file.end - file.start, fontFileDict);
-
- // Check if this is a FlateStream. Otherwise just use the created
- // Stream one. This makes complex_ttf_font.pdf work.
- var cmf = file.bytes[0];
- if ((cmf & 0x0f) == 0x08) {
- file = new FlateStream(fontFile);
- } else {
- file = fontFile;
- }
+ file = new Stream(file, 0, file.length, fontFileDict);
}
- // For now, resolve the font object here direclty. The real font
- // object is then created in FontLoader.bind().
- this.objs.resolve(id, {
- name: name,
- file: file,
- properties: properties
- });
+ // At this point, only the font object is created but the font is
+ // not yet attached to the DOM. This is done in `FontLoader.bind`.
+ var font = new Font(name, file, properties);
+ this.objs.resolve(id, font);
break;
default:
- throw 'Got unkown object type ' + type;
+ error('Got unkown object type ' + type);
}
}, this);
- messageHandler.on('font_ready', function pdfDocFontReady(data) {
- var id = data[0];
- var font = new FontShape(data[1]);
+ messageHandler.on('page_error', function pdfDocError(data) {
+ var page = this.pageCache[data.pageNum];
+ if (page.displayReadyPromise)
+ page.displayReadyPromise.reject(data.error);
+ else
+ error(data.error);
+ }, this);
- // If there is no string, then there is nothing to attach to the DOM.
- if (!font.str) {
- this.objs.resolve(id, font);
- } else {
- this.objs.setData(id, font);
- }
- }.bind(this));
+ messageHandler.on('jpeg_decode', function(data, promise) {
+ var imageData = data[0];
+ var components = data[1];
+ if (components != 3 && components != 1)
+ error('Only 3 component or 1 component can be returned');
+
+ var img = new Image();
+ img.onload = (function messageHandler_onloadClosure() {
+ var width = img.width;
+ var height = img.height;
+ var size = width * height;
+ var rgbaLength = size * 4;
+ var buf = new Uint8Array(size * components);
+ var tmpCanvas = createScratchCanvas(width, height);
+ var tmpCtx = tmpCanvas.getContext('2d');
+ tmpCtx.drawImage(img, 0, 0);
+ var data = tmpCtx.getImageData(0, 0, width, height).data;
+
+ if (components == 3) {
+ for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
+ buf[j] = data[i];
+ buf[j + 1] = data[i + 1];
+ buf[j + 2] = data[i + 2];
+ }
+ } else if (components == 1) {
+ for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
+ buf[j] = data[i];
+ }
+ }
+ promise.resolve({ data: buf, width: width, height: height});
+ }).bind(this);
+ var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
+ img.src = src;
+ });
setTimeout(function pdfDocFontReadySetTimeout() {
messageHandler.send('doc', this.data);
@@ -619,21 +846,22 @@ var PDFDoc = (function pdfDoc() {
},
get numPages() {
- return this.pdf.numPages;
+ return this.pdfModel.numPages;
},
- startRendering: function pdfDocStartRendering(page) {
+ startRendering: function PDFDoc_startRendering(page) {
// The worker might not be ready to receive the page request yet.
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
+ page.stats.time('Page Request');
this.messageHandler.send('page_request', page.pageNumber + 1);
}.bind(this));
},
- getPage: function pdfDocGetPage(n) {
+ getPage: function PDFDoc_getPage(n) {
if (this.pageCache[n])
return this.pageCache[n];
- var page = this.pdf.getPage(n);
+ var page = this.pdfModel.getPage(n);
// Add a reference to the objects such that Page can forward the reference
// to the CanvasGraphics and so on.
page.objs = this.objs;
@@ -641,7 +869,7 @@ var PDFDoc = (function pdfDoc() {
return (this.pageCache[n] = page);
},
- destroy: function pdfDocDestroy() {
+ destroy: function PDFDoc_destroy() {
if (this.worker)
this.worker.terminate();
@@ -658,7 +886,7 @@ var PDFDoc = (function pdfDoc() {
}
};
- return constructor;
+ return PDFDoc;
})();
globalScope.PDFJS.PDFDoc = PDFDoc;
@@ -741,24 +969,102 @@ function stringToBytes(str) {
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
-var Util = (function utilUtil() {
- function constructor() {}
- constructor.makeCssRgb = function makergb(r, g, b) {
+var Util = (function UtilClosure() {
+ function Util() {}
+
+ Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
};
- constructor.makeCssCmyk = function makecmyk(c, m, y, k) {
+
+ Util.makeCssCmyk = function Util_makeCssCmyk(c, m, y, k) {
c = (new DeviceCmykCS()).getRgb([c, m, y, k]);
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
};
- constructor.applyTransform = function apply(p, m) {
+
+ // For 2d affine transforms
+ Util.applyTransform = function Util_applyTransform(p, m) {
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
return [xt, yt];
};
- return constructor;
+ // Apply a generic 3d matrix M on a 3-vector v:
+ // | a b c | | X |
+ // | d e f | x | Y |
+ // | g h i | | Z |
+ // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
+ // with v as [X,Y,Z]
+ Util.apply3dTransform = function Util_apply3dTransform(m, v) {
+ return [
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
+ m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
+ m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
+ ];
+ }
+
+ // Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
+ // For coordinate systems whose origin lies in the bottom-left, this
+ // means normalization to (BL,TR) ordering. For systems with origin in the
+ // top-left, this means (TL,BR) ordering.
+ Util.normalizeRect = function Util_normalizeRect(rect) {
+ var r = rect.slice(0); // clone rect
+ if (rect[0] > rect[2]) {
+ r[0] = rect[2];
+ r[2] = rect[0];
+ }
+ if (rect[1] > rect[3]) {
+ r[1] = rect[3];
+ r[3] = rect[1];
+ }
+ return r;
+ }
+
+ // Returns a rectangle [x1, y1, x2, y2] corresponding to the
+ // intersection of rect1 and rect2. If no intersection, returns 'false'
+ // The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
+ Util.intersect = function Util_intersect(rect1, rect2) {
+ function compare(a, b) {
+ return a - b;
+ };
+
+ // Order points along the axes
+ var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
+ orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
+ result = [];
+
+ rect1 = Util.normalizeRect(rect1);
+ rect2 = Util.normalizeRect(rect2);
+
+ // X: first and second points belong to different rectangles?
+ if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
+ (orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
+ // Intersection must be between second and third points
+ result[0] = orderedX[1];
+ result[2] = orderedX[2];
+ } else {
+ return false;
+ }
+
+ // Y: first and second points belong to different rectangles?
+ if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
+ (orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
+ // Intersection must be between second and third points
+ result[1] = orderedY[1];
+ result[3] = orderedY[2];
+ } else {
+ return false;
+ }
+
+ return result;
+ }
+
+ Util.sign = function Util_sign(num) {
+ return num < 0 ? -1 : 1;
+ };
+
+ return Util;
})();
var PDFStringTranslateTable = [
@@ -862,7 +1168,7 @@ function isPDFFunction(v) {
* can be set. If any of these happens twice or the data is required before
* it was set, an exception is throw.
*/
-var Promise = (function promise() {
+var Promise = (function PromiseClosure() {
var EMPTY_PROMISE = {};
/**
@@ -871,6 +1177,8 @@ var Promise = (function promise() {
*/
function Promise(name, data) {
this.name = name;
+ this.isRejected = false;
+ this.error = null;
// If you build a promise and pass in some data it's already resolved.
if (data != null) {
this.isResolved = true;
@@ -881,8 +1189,35 @@ var Promise = (function promise() {
this._data = EMPTY_PROMISE;
}
this.callbacks = [];
+ this.errbacks = [];
+ };
+ /**
+ * Builds a promise that is resolved when all the passed in promises are
+ * resolved.
+ * @param {Promise[]} promises Array of promises to wait for.
+ * @return {Promise} New dependant promise.
+ */
+ Promise.all = function Promise_all(promises) {
+ var deferred = new Promise();
+ var unresolved = promises.length;
+ var results = [];
+ if (unresolved === 0) {
+ deferred.resolve(results);
+ return deferred;
+ }
+ for (var i = 0; i < unresolved; ++i) {
+ var promise = promises[i];
+ promise.then((function(i) {
+ return function(value) {
+ results[i] = value;
+ unresolved--;
+ if (unresolved === 0)
+ deferred.resolve(results);
+ };
+ })(i));
+ }
+ return deferred;
};
-
Promise.prototype = {
hasData: false,
@@ -891,8 +1226,8 @@ var Promise = (function promise() {
return;
}
if (this._data !== EMPTY_PROMISE) {
- throw 'Promise ' + this.name +
- ': Cannot set the data of a promise twice';
+ error('Promise ' + this.name +
+ ': Cannot set the data of a promise twice');
}
this._data = value;
this.hasData = true;
@@ -904,12 +1239,12 @@ var Promise = (function promise() {
get data() {
if (this._data === EMPTY_PROMISE) {
- throw 'Promise ' + this.name + ': Cannot get data that isn\'t set';
+ error('Promise ' + this.name + ': Cannot get data that isn\'t set');
}
return this._data;
},
- onData: function promiseOnData(callback) {
+ onData: function Promise_onData(callback) {
if (this._data !== EMPTY_PROMISE) {
callback(this._data);
} else {
@@ -917,13 +1252,16 @@ var Promise = (function promise() {
}
},
- resolve: function promiseResolve(data) {
+ resolve: function Promise_resolve(data) {
if (this.isResolved) {
- throw 'A Promise can be resolved only once ' + this.name;
+ error('A Promise can be resolved only once ' + this.name);
+ }
+ if (this.isRejected) {
+ error('The Promise was already rejected ' + this.name);
}
this.isResolved = true;
- this.data = data;
+ this.data = data || null;
var callbacks = this.callbacks;
for (var i = 0, ii = callbacks.length; i < ii; i++) {
@@ -931,17 +1269,39 @@ var Promise = (function promise() {
}
},
- then: function promiseThen(callback) {
+ reject: function Promise_reject(reason) {
+ if (this.isRejected) {
+ error('A Promise can be rejected only once ' + this.name);
+ }
+ if (this.isResolved) {
+ error('The Promise was already resolved ' + this.name);
+ }
+
+ this.isRejected = true;
+ this.error = reason || null;
+ var errbacks = this.errbacks;
+
+ for (var i = 0, ii = errbacks.length; i < ii; i++) {
+ errbacks[i].call(null, reason);
+ }
+ },
+
+ then: function Promise_then(callback, errback) {
if (!callback) {
- throw 'Requiring callback' + this.name;
+ error('Requiring callback' + this.name);
}
// If the promise is already resolved, call the callback directly.
if (this.isResolved) {
var data = this.data;
callback.call(null, data);
+ } else if (this.isRejected && errback) {
+ var error = this.error;
+ errback.call(null, error);
} else {
this.callbacks.push(callback);
+ if (errback)
+ this.errbacks.push(errback);
}
}
};
@@ -949,6 +1309,58 @@ var Promise = (function promise() {
return Promise;
})();
+var StatTimer = (function StatTimerClosure() {
+ function rpad(str, pad, length) {
+ while (str.length < length)
+ str += pad;
+ return str;
+ }
+ function StatTimer() {
+ this.started = {};
+ this.times = [];
+ this.enabled = true;
+ }
+ StatTimer.prototype = {
+ time: function StatTimer_time(name) {
+ if (!this.enabled)
+ return;
+ if (name in this.started)
+ throw 'Timer is already running for ' + name;
+ this.started[name] = Date.now();
+ },
+ timeEnd: function StatTimer_timeEnd(name) {
+ if (!this.enabled)
+ return;
+ if (!(name in this.started))
+ throw 'Timer has not been started for ' + name;
+ this.times.push({
+ 'name': name,
+ 'start': this.started[name],
+ 'end': Date.now()
+ });
+ // Remove timer from started so it can be called again.
+ delete this.started[name];
+ },
+ toString: function StatTimer_toString() {
+ var times = this.times;
+ var out = '';
+ // Find the longest name for padding purposes.
+ var longest = 0;
+ for (var i = 0, ii = times.length; i < ii; ++i) {
+ var name = times[i]['name'];
+ if (name.length > longest)
+ longest = name.length;
+ }
+ for (var i = 0, ii = times.length; i < ii; ++i) {
+ var span = times[i];
+ var duration = span.end - span.start;
+ out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
+ }
+ return out;
+ }
+ };
+ return StatTimer;
+})();
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
@@ -957,12 +1369,145 @@ var Promise = (function promise() {
//
"),j=this.$getTop(b.end.row,d),i=Math.round(b.end.column*d.characterWidth),a.push("
"),h=(b.end.row-b.start.row-1)*d.lineHeight;if(h<0)return;j=this.$getTop(b.start.row+1,d),a.push("
")},this.drawSingleLineMarker=function(a,b,c,d,e,f){var g=f==="background"?0:this.$padding,h=d.lineHeight;if(f==="background")var i=d.width;else i=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth);var j=this.$getTop(b.start.row,d),k=Math.round(g+b.start.column*d.characterWidth);a.push("
")}})).call(f.prototype),b.Marker=f}),define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(a,b,c){"use strict";var d=a("../lib/oop"),e=a("../lib/dom"),f=a("../lib/lang"),g=a("../lib/useragent"),h=a("../lib/event_emitter").EventEmitter,i=function(a){this.element=e.createElement("div"),this.element.className="ace_layer ace_text-layer",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};((function(){d.implement(this,h),this.EOF_CHAR="¶",this.EOL_CHAR="¬",this.TAB_CHAR="→",this.SPACE_CHAR="·",this.$padding=0,this.setPadding=function(a){this.$padding=a,this.element.style.padding="0 "+a+"px"},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._emit("changeCharaterSize",{data:a}))},this.$pollSizeChanges=function(){var a=this;this.$pollSizeChangesTimer=setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement("div"),c=b.style;c.width=c.height="auto",c.left=c.top=-a*40+"px",c.visibility="hidden",c.position="absolute",c.overflow="visible",c.whiteSpace="nowrap",b.innerHTML=f.stringRepeat("Xy",a);if(this.element.ownerDocument.body)this.element.ownerDocument.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,"ace_editor"))d=d.parentNode;d.appendChild(b)}}var c=this.$measureNode.style,g=e.computedStyle(this.element);for(var h in this.$fontStyles)c[h]=g[h];var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};return i.width==0&&i.height==0?null:i},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){return this.showInvisibles==a?!1:(this.showInvisibles=a,!0)},this.$tabStrings=[],this.$computeTabString=function(){var a=this.session.getTabSize(),b=this.$tabStrings=[0];for(var c=1;c