mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-12 06:02:39 +01:00
260 lines
7.7 KiB
JavaScript
260 lines
7.7 KiB
JavaScript
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||
|
/* 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.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
// optimised CSS custom property getter/setter
|
||
|
var CustomStyle = (function CustomStyleClosure() {
|
||
|
|
||
|
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
||
|
// animate-css-transforms-firefox-webkit.html
|
||
|
// in some versions of IE9 it is critical that ms appear in this list
|
||
|
// before Moz
|
||
|
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||
|
var _cache = {};
|
||
|
|
||
|
function CustomStyle() {}
|
||
|
|
||
|
CustomStyle.getProp = function get(propName, element) {
|
||
|
// check cache only when no element is given
|
||
|
if (arguments.length == 1 && typeof _cache[propName] == 'string') {
|
||
|
return _cache[propName];
|
||
|
}
|
||
|
|
||
|
element = element || document.documentElement;
|
||
|
var style = element.style, prefixed, uPropName;
|
||
|
|
||
|
// test standard property first
|
||
|
if (typeof style[propName] == 'string') {
|
||
|
return (_cache[propName] = propName);
|
||
|
}
|
||
|
|
||
|
// capitalize
|
||
|
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
||
|
|
||
|
// test vendor specific properties
|
||
|
for (var i = 0, l = prefixes.length; i < l; i++) {
|
||
|
prefixed = prefixes[i] + uPropName;
|
||
|
if (typeof style[prefixed] == 'string') {
|
||
|
return (_cache[propName] = prefixed);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//if all fails then set to undefined
|
||
|
return (_cache[propName] = 'undefined');
|
||
|
};
|
||
|
|
||
|
CustomStyle.setProp = function set(propName, element, str) {
|
||
|
var prop = this.getProp(propName);
|
||
|
if (prop != 'undefined') {
|
||
|
element.style[prop] = str;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return CustomStyle;
|
||
|
})();
|
||
|
|
||
|
function getFileName(url) {
|
||
|
var anchor = url.indexOf('#');
|
||
|
var query = url.indexOf('?');
|
||
|
var end = Math.min(
|
||
|
anchor > 0 ? anchor : url.length,
|
||
|
query > 0 ? query : url.length);
|
||
|
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns scale factor for the canvas. It makes sense for the HiDPI displays.
|
||
|
* @return {Object} The object with horizontal (sx) and vertical (sy)
|
||
|
scales. The scaled property is set to false if scaling is
|
||
|
not required, true otherwise.
|
||
|
*/
|
||
|
function getOutputScale(ctx) {
|
||
|
var devicePixelRatio = window.devicePixelRatio || 1;
|
||
|
var backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
||
|
ctx.mozBackingStorePixelRatio ||
|
||
|
ctx.msBackingStorePixelRatio ||
|
||
|
ctx.oBackingStorePixelRatio ||
|
||
|
ctx.backingStorePixelRatio || 1;
|
||
|
var pixelRatio = devicePixelRatio / backingStoreRatio;
|
||
|
return {
|
||
|
sx: pixelRatio,
|
||
|
sy: pixelRatio,
|
||
|
scaled: pixelRatio != 1
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Scrolls specified element into view of its parent.
|
||
|
* element {Object} The element to be visible.
|
||
|
* spot {Object} An object with optional top and left properties,
|
||
|
* specifying the offset from the top left edge.
|
||
|
*/
|
||
|
function scrollIntoView(element, spot) {
|
||
|
// Assuming offsetParent is available (it's not available when viewer is in
|
||
|
// hidden iframe or object). We have to scroll: if the offsetParent is not set
|
||
|
// producing the error. See also animationStartedClosure.
|
||
|
var parent = element.offsetParent;
|
||
|
var offsetY = element.offsetTop + element.clientTop;
|
||
|
var offsetX = element.offsetLeft + element.clientLeft;
|
||
|
if (!parent) {
|
||
|
console.error('offsetParent is not set -- cannot scroll');
|
||
|
return;
|
||
|
}
|
||
|
while (parent.clientHeight === parent.scrollHeight) {
|
||
|
if (parent.dataset._scaleY) {
|
||
|
offsetY /= parent.dataset._scaleY;
|
||
|
offsetX /= parent.dataset._scaleX;
|
||
|
}
|
||
|
offsetY += parent.offsetTop;
|
||
|
offsetX += parent.offsetLeft;
|
||
|
parent = parent.offsetParent;
|
||
|
if (!parent) {
|
||
|
return; // no need to scroll
|
||
|
}
|
||
|
}
|
||
|
if (spot) {
|
||
|
if (spot.top !== undefined) {
|
||
|
offsetY += spot.top;
|
||
|
}
|
||
|
if (spot.left !== undefined) {
|
||
|
offsetX += spot.left;
|
||
|
parent.scrollLeft = offsetX;
|
||
|
}
|
||
|
}
|
||
|
parent.scrollTop = offsetY;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Event handler to suppress context menu.
|
||
|
*/
|
||
|
function noContextMenuHandler(e) {
|
||
|
e.preventDefault();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the filename or guessed filename from the url (see issue 3455).
|
||
|
* url {String} The original PDF location.
|
||
|
* @return {String} Guessed PDF file name.
|
||
|
*/
|
||
|
function getPDFFileNameFromURL(url) {
|
||
|
var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
|
||
|
// SCHEME HOST 1.PATH 2.QUERY 3.REF
|
||
|
// Pattern to get last matching NAME.pdf
|
||
|
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
|
||
|
var splitURI = reURI.exec(url);
|
||
|
var suggestedFilename = reFilename.exec(splitURI[1]) ||
|
||
|
reFilename.exec(splitURI[2]) ||
|
||
|
reFilename.exec(splitURI[3]);
|
||
|
if (suggestedFilename) {
|
||
|
suggestedFilename = suggestedFilename[0];
|
||
|
if (suggestedFilename.indexOf('%') != -1) {
|
||
|
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
|
||
|
try {
|
||
|
suggestedFilename =
|
||
|
reFilename.exec(decodeURIComponent(suggestedFilename))[0];
|
||
|
} catch(e) { // Possible (extremely rare) errors:
|
||
|
// URIError "Malformed URI", e.g. for "%AA.pdf"
|
||
|
// TypeError "null has no properties", e.g. for "%2F.pdf"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return suggestedFilename || 'document.pdf';
|
||
|
}
|
||
|
|
||
|
var ProgressBar = (function ProgressBarClosure() {
|
||
|
|
||
|
function clamp(v, min, max) {
|
||
|
return Math.min(Math.max(v, min), max);
|
||
|
}
|
||
|
|
||
|
function ProgressBar(id, opts) {
|
||
|
|
||
|
// Fetch the sub-elements for later.
|
||
|
this.div = document.querySelector(id + ' .progress');
|
||
|
|
||
|
// Get the loading bar element, so it can be resized to fit the viewer.
|
||
|
this.bar = this.div.parentNode;
|
||
|
|
||
|
// Get options, with sensible defaults.
|
||
|
this.height = opts.height || 100;
|
||
|
this.width = opts.width || 100;
|
||
|
this.units = opts.units || '%';
|
||
|
|
||
|
// Initialize heights.
|
||
|
this.div.style.height = this.height + this.units;
|
||
|
this.percent = 0;
|
||
|
}
|
||
|
|
||
|
ProgressBar.prototype = {
|
||
|
|
||
|
updateBar: function ProgressBar_updateBar() {
|
||
|
if (this._indeterminate) {
|
||
|
this.div.classList.add('indeterminate');
|
||
|
this.div.style.width = this.width + this.units;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.div.classList.remove('indeterminate');
|
||
|
var progressSize = this.width * this._percent / 100;
|
||
|
this.div.style.width = progressSize + this.units;
|
||
|
},
|
||
|
|
||
|
get percent() {
|
||
|
return this._percent;
|
||
|
},
|
||
|
|
||
|
set percent(val) {
|
||
|
this._indeterminate = isNaN(val);
|
||
|
this._percent = clamp(val, 0, 100);
|
||
|
this.updateBar();
|
||
|
},
|
||
|
|
||
|
setWidth: function ProgressBar_setWidth(viewer) {
|
||
|
if (viewer) {
|
||
|
var container = viewer.parentNode;
|
||
|
var scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
|
||
|
if (scrollbarWidth > 0) {
|
||
|
this.bar.setAttribute('style', 'width: calc(100% - ' +
|
||
|
scrollbarWidth + 'px);');
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
hide: function ProgressBar_hide() {
|
||
|
this.bar.classList.add('hidden');
|
||
|
this.bar.removeAttribute('style');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
return ProgressBar;
|
||
|
})();
|
||
|
|
||
|
var Cache = function cacheCache(size) {
|
||
|
var data = [];
|
||
|
this.push = function cachePush(view) {
|
||
|
var i = data.indexOf(view);
|
||
|
if (i >= 0) {
|
||
|
data.splice(i);
|
||
|
}
|
||
|
data.push(view);
|
||
|
if (data.length > size) {
|
||
|
data.shift().destroy();
|
||
|
}
|
||
|
};
|
||
|
};
|
||
|
|