/* -*- 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. */ // Initializing PDFJS global object (if still undefined) if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } PDFJS.version = '0.8.765'; PDFJS.build = '88ec2bd'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it 'use strict'; /* -*- 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. */ /* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */ 'use strict'; var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); var ERRORS = 0, WARNINGS = 1, INFOS = 5; var verbosity = WARNINGS; var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; var TextRenderingMode = { FILL: 0, STROKE: 1, FILL_STROKE: 2, INVISIBLE: 3, FILL_ADD_TO_PATH: 4, STROKE_ADD_TO_PATH: 5, FILL_STROKE_ADD_TO_PATH: 6, ADD_TO_PATH: 7, FILL_STROKE_MASK: 3, ADD_TO_PATH_FLAG: 4 }; // The global PDFJS object exposes the API // In production, it will be declared outside a global wrapper // In development, it will be declared here if (!globalScope.PDFJS) { globalScope.PDFJS = {}; } globalScope.PDFJS.pdfBug = false; // All the possible operations for an operator list. var OPS = PDFJS.OPS = { // Intentionally start from 1 so it is easy to spot bad operators that will be // 0's. dependency: 1, setLineWidth: 2, setLineCap: 3, setLineJoin: 4, setMiterLimit: 5, setDash: 6, setRenderingIntent: 7, setFlatness: 8, setGState: 9, save: 10, restore: 11, transform: 12, moveTo: 13, lineTo: 14, curveTo: 15, curveTo2: 16, curveTo3: 17, closePath: 18, rectangle: 19, stroke: 20, closeStroke: 21, fill: 22, eoFill: 23, fillStroke: 24, eoFillStroke: 25, closeFillStroke: 26, closeEOFillStroke: 27, endPath: 28, clip: 29, eoClip: 30, beginText: 31, endText: 32, setCharSpacing: 33, setWordSpacing: 34, setHScale: 35, setLeading: 36, setFont: 37, setTextRenderingMode: 38, setTextRise: 39, moveText: 40, setLeadingMoveText: 41, setTextMatrix: 42, nextLine: 43, showText: 44, showSpacedText: 45, nextLineShowText: 46, nextLineSetSpacingShowText: 47, setCharWidth: 48, setCharWidthAndBounds: 49, setStrokeColorSpace: 50, setFillColorSpace: 51, setStrokeColor: 52, setStrokeColorN: 53, setFillColor: 54, setFillColorN: 55, setStrokeGray: 56, setFillGray: 57, setStrokeRGBColor: 58, setFillRGBColor: 59, setStrokeCMYKColor: 60, setFillCMYKColor: 61, shadingFill: 62, beginInlineImage: 63, beginImageData: 64, endInlineImage: 65, paintXObject: 66, markPoint: 67, markPointProps: 68, beginMarkedContent: 69, beginMarkedContentProps: 70, endMarkedContent: 71, beginCompat: 72, endCompat: 73, paintFormXObjectBegin: 74, paintFormXObjectEnd: 75, beginGroup: 76, endGroup: 77, beginAnnotations: 78, endAnnotations: 79, beginAnnotation: 80, endAnnotation: 81, paintJpegXObject: 82, paintImageMaskXObject: 83, paintImageMaskXObjectGroup: 84, paintImageXObject: 85, paintInlineImageXObject: 86, paintInlineImageXObjectGroup: 87 }; // Use only for debugging purposes. This should not be used in any code that is // in mozilla master. var log = (function() { if ('console' in globalScope && 'log' in globalScope['console']) { return globalScope['console']['log'].bind(globalScope['console']); } else { return function nop() { }; } })(); // A notice for devs that will not trigger the fallback UI. These are good // for things that are helpful to devs, such as warning that Workers were // disabled, which is important to devs but not end users. function info(msg) { if (verbosity >= INFOS) { log('Info: ' + msg); PDFJS.LogManager.notify('info', msg); } } // Non-fatal warnings that should trigger the fallback UI. function warn(msg) { if (verbosity >= WARNINGS) { log('Warning: ' + msg); PDFJS.LogManager.notify('warn', msg); } } // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { // If multiple arguments were passed, pass them all to the log function. if (arguments.length > 1) { var logArguments = ['Error:']; logArguments.push.apply(logArguments, arguments); log.apply(null, logArguments); // Join the arguments into a single string for the lines below. msg = [].join.call(arguments, ' '); } else { log('Error: ' + msg); } log(backtrace()); PDFJS.LogManager.notify('error', msg); throw new Error(msg); } // Missing features that should trigger the fallback UI. function TODO(what) { warn('TODO: ' + what); } function backtrace() { try { throw new Error(); } catch (e) { return e.stack ? e.stack.split('\n').slice(2).join('\n') : ''; } } function assert(cond, msg) { if (!cond) error(msg); } // Combines two URLs. The baseUrl shall be absolute URL. If the url is an // absolute URL, it will be returned as is. function combineUrl(baseUrl, url) { if (!url) return baseUrl; if (url.indexOf(':') >= 0) return url; if (url.charAt(0) == '/') { // absolute path var i = baseUrl.indexOf('://'); i = baseUrl.indexOf('/', i + 3); return baseUrl.substring(0, i) + url; } else { // relative path var pathLength = baseUrl.length, i; i = baseUrl.lastIndexOf('#'); pathLength = i >= 0 ? i : pathLength; i = baseUrl.lastIndexOf('?', pathLength); pathLength = i >= 0 ? i : pathLength; var prefixLength = baseUrl.lastIndexOf('/', pathLength); return baseUrl.substring(0, prefixLength + 1) + url; } } // Validates if URL is safe and allowed, e.g. to avoid XSS. function isValidUrl(url, allowRelative) { if (!url) { return false; } var colon = url.indexOf(':'); if (colon < 0) { return allowRelative; } var protocol = url.substr(0, colon); switch (protocol) { case 'http': case 'https': case 'ftp': case 'mailto': return true; default: return false; } } PDFJS.isValidUrl = isValidUrl; // In a well-formed PDF, |cond| holds. If it doesn't, subsequent // behavior is undefined. function assertWellFormed(cond, msg) { if (!cond) error(msg); } var LogManager = PDFJS.LogManager = (function LogManagerClosure() { var loggers = []; return { addLogger: function logManager_addLogger(logger) { loggers.push(logger); }, notify: function(type, message) { for (var i = 0, ii = loggers.length; i < ii; i++) { var logger = loggers[i]; if (logger[type]) logger[type](message); } } }; })(); function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, configurable: true, writable: false }); return value; } var PasswordResponses = PDFJS.PasswordResponses = { NEED_PASSWORD: 1, INCORRECT_PASSWORD: 2 }; var PasswordException = (function PasswordExceptionClosure() { function PasswordException(msg, code) { this.name = 'PasswordException'; this.message = msg; this.code = code; } PasswordException.prototype = new Error(); PasswordException.constructor = PasswordException; return PasswordException; })(); var UnknownErrorException = (function UnknownErrorExceptionClosure() { function UnknownErrorException(msg, details) { this.name = 'UnknownErrorException'; this.message = msg; this.details = details; } UnknownErrorException.prototype = new Error(); UnknownErrorException.constructor = UnknownErrorException; return UnknownErrorException; })(); var InvalidPDFException = (function InvalidPDFExceptionClosure() { function InvalidPDFException(msg) { this.name = 'InvalidPDFException'; this.message = msg; } InvalidPDFException.prototype = new Error(); InvalidPDFException.constructor = InvalidPDFException; return InvalidPDFException; })(); var MissingPDFException = (function MissingPDFExceptionClosure() { function MissingPDFException(msg) { this.name = 'MissingPDFException'; this.message = msg; } MissingPDFException.prototype = new Error(); MissingPDFException.constructor = MissingPDFException; return MissingPDFException; })(); var NotImplementedException = (function NotImplementedExceptionClosure() { function NotImplementedException(msg) { this.message = msg; } NotImplementedException.prototype = new Error(); NotImplementedException.prototype.name = 'NotImplementedException'; NotImplementedException.constructor = NotImplementedException; return NotImplementedException; })(); var MissingDataException = (function MissingDataExceptionClosure() { function MissingDataException(begin, end) { this.begin = begin; this.end = end; this.message = 'Missing data [' + begin + ', ' + end + ')'; } MissingDataException.prototype = new Error(); MissingDataException.prototype.name = 'MissingDataException'; MissingDataException.constructor = MissingDataException; return MissingDataException; })(); var XRefParseException = (function XRefParseExceptionClosure() { function XRefParseException(msg) { this.message = msg; } XRefParseException.prototype = new Error(); XRefParseException.prototype.name = 'XRefParseException'; XRefParseException.constructor = XRefParseException; return XRefParseException; })(); function bytesToString(bytes) { var str = ''; var length = bytes.length; for (var n = 0; n < length; ++n) str += String.fromCharCode(bytes[n]); return str; } function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); for (var n = 0; n < length; ++n) bytes[n] = str.charCodeAt(n) & 0xFF; return bytes; } var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var Util = PDFJS.Util = (function UtilClosure() { function Util() {} Util.makeCssRgb = function Util_makeCssRgb(rgb) { return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'; }; Util.makeCssCmyk = function Util_makeCssCmyk(cmyk) { var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0); return Util.makeCssRgb(rgb); }; // Concatenates two transformation matrices together and returns the result. Util.transform = function Util_transform(m1, m2) { return [ m1[0] * m2[0] + m1[2] * m2[1], m1[1] * m2[0] + m1[3] * m2[1], m1[0] * m2[2] + m1[2] * m2[3], m1[1] * m2[2] + m1[3] * m2[3], m1[0] * m2[4] + m1[2] * m2[5] + m1[4], m1[1] * m2[4] + m1[3] * m2[5] + m1[5] ]; }; // 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]; }; Util.applyInverseTransform = function Util_applyInverseTransform(p, m) { var d = m[0] * m[3] - m[1] * m[2]; var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d; var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d; return [xt, yt]; }; // Applies the transform to the rectangle and finds the minimum axially // aligned bounding box. Util.getAxialAlignedBoundingBox = function Util_getAxialAlignedBoundingBox(r, m) { var p1 = Util.applyTransform(r, m); var p2 = Util.applyTransform(r.slice(2, 4), m); var p3 = Util.applyTransform([r[0], r[3]], m); var p4 = Util.applyTransform([r[2], r[1]], m); return [ Math.min(p1[0], p2[0], p3[0], p4[0]), Math.min(p1[1], p2[1], p3[1], p4[1]), Math.max(p1[0], p2[0], p3[0], p4[0]), Math.max(p1[1], p2[1], p3[1], p4[1]) ]; }; Util.inverseTransform = function Util_inverseTransform(m) { var d = m[0] * m[3] - m[1] * m[2]; return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d, (m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d]; }; // 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] ]; }; // This calculation uses Singular Value Decomposition. // The SVD can be represented with formula A = USV. We are interested in the // matrix S here because it represents the scale values. Util.singularValueDecompose2dScale = function Util_singularValueDecompose2dScale(m) { var transpose = [m[0], m[2], m[1], m[3]]; // Multiply matrix m with its transpose. var a = m[0] * transpose[0] + m[1] * transpose[2]; var b = m[0] * transpose[1] + m[1] * transpose[3]; var c = m[2] * transpose[0] + m[3] * transpose[2]; var d = m[2] * transpose[1] + m[3] * transpose[3]; // Solve the second degree polynomial to get roots. var first = (a + d) / 2; var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2; var sx = first + second || 1; var sy = first - second || 1; // Scale values are the square roots of the eigenvalues. return [Math.sqrt(sx), Math.sqrt(sy)]; }; // 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; }; // TODO(mack): Rename appendToArray Util.concatenateToArray = function concatenateToArray(arr1, arr2) { Array.prototype.push.apply(arr1, arr2); }; Util.prependToArray = function concatenateToArray(arr1, arr2) { Array.prototype.unshift.apply(arr1, arr2); }; Util.extendObj = function extendObj(obj1, obj2) { for (var key in obj2) { obj1[key] = obj2[key]; } }; Util.getInheritableProperty = function Util_getInheritableProperty(dict, name) { while (dict && !dict.has(name)) { dict = dict.get('Parent'); } if (!dict) { return null; } return dict.get(name); }; Util.inherit = function Util_inherit(sub, base, prototype) { sub.prototype = Object.create(base.prototype); sub.prototype.constructor = sub; for (var prop in prototype) { sub.prototype[prop] = prototype[prop]; } }; Util.loadScript = function Util_loadScript(src, callback) { var script = document.createElement('script'); var loaded = false; script.setAttribute('src', src); if (callback) { script.onload = function() { if (!loaded) { callback(); } loaded = true; }; } document.getElementsByTagName('head')[0].appendChild(script); }; return Util; })(); var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() { function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) { this.viewBox = viewBox; this.scale = scale; this.rotation = rotation; this.offsetX = offsetX; this.offsetY = offsetY; // creating transform to convert pdf coordinate system to the normal // canvas like coordinates taking in account scale and rotation var centerX = (viewBox[2] + viewBox[0]) / 2; var centerY = (viewBox[3] + viewBox[1]) / 2; var rotateA, rotateB, rotateC, rotateD; rotation = rotation % 360; rotation = rotation < 0 ? rotation + 360 : rotation; switch (rotation) { case 180: rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1; break; case 90: rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0; break; case 270: rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0; break; //case 0: default: rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1; break; } if (dontFlip) { rotateC = -rotateC; rotateD = -rotateD; } var offsetCanvasX, offsetCanvasY; var width, height; if (rotateA === 0) { offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX; offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY; width = Math.abs(viewBox[3] - viewBox[1]) * scale; height = Math.abs(viewBox[2] - viewBox[0]) * scale; } else { offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX; offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY; width = Math.abs(viewBox[2] - viewBox[0]) * scale; height = Math.abs(viewBox[3] - viewBox[1]) * scale; } // creating transform for the following operations: // translate(-centerX, -centerY), rotate and flip vertically, // scale, and translate(offsetCanvasX, offsetCanvasY) this.transform = [ rotateA * scale, rotateB * scale, rotateC * scale, rotateD * scale, offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY, offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY ]; this.width = width; this.height = height; this.fontScale = scale; } PageViewport.prototype = { clone: function PageViewPort_clone(args) { args = args || {}; var scale = 'scale' in args ? args.scale : this.scale; var rotation = 'rotation' in args ? args.rotation : this.rotation; return new PageViewport(this.viewBox.slice(), scale, rotation, this.offsetX, this.offsetY, args.dontFlip); }, convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) { return Util.applyTransform([x, y], this.transform); }, convertToViewportRectangle: function PageViewport_convertToViewportRectangle(rect) { var tl = Util.applyTransform([rect[0], rect[1]], this.transform); var br = Util.applyTransform([rect[2], rect[3]], this.transform); return [tl[0], tl[1], br[0], br[1]]; }, convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) { return Util.applyInverseTransform([x, y], this.transform); } }; return PageViewport; })(); var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C, 0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160, 0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC ]; function stringToPDFString(str) { var i, n = str.length, str2 = ''; if (str[0] === '\xFE' && str[1] === '\xFF') { // UTF16BE BOM for (i = 2; i < n; i += 2) str2 += String.fromCharCode( (str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)); } else { for (i = 0; i < n; ++i) { var code = PDFStringTranslateTable[str.charCodeAt(i)]; str2 += code ? String.fromCharCode(code) : str.charAt(i); } } return str2; } function stringToUTF8String(str) { return decodeURIComponent(escape(str)); } function isEmptyObj(obj) { for (var key in obj) { return false; } return true; } function isBool(v) { return typeof v == 'boolean'; } function isInt(v) { return typeof v == 'number' && ((v | 0) == v); } function isNum(v) { return typeof v == 'number'; } function isString(v) { return typeof v == 'string'; } function isNull(v) { return v === null; } function isName(v) { return v instanceof Name; } function isCmd(v, cmd) { return v instanceof Cmd && (!cmd || v.cmd == cmd); } function isDict(v, type) { if (!(v instanceof Dict)) { return false; } if (!type) { return true; } var dictType = v.get('Type'); return isName(dictType) && dictType.name == type; } function isArray(v) { return v instanceof Array; } function isStream(v) { return typeof v == 'object' && v !== null && v !== undefined && ('getBytes' in v); } function isArrayBuffer(v) { return typeof v == 'object' && v !== null && v !== undefined && ('byteLength' in v); } function isRef(v) { return v instanceof Ref; } function isPDFFunction(v) { var fnDict; if (typeof v != 'object') return false; else if (isDict(v)) fnDict = v; else if (isStream(v)) fnDict = v.dict; else return false; return fnDict.has('FunctionType'); } /** * The following promise implementation tries to generally implment the * Promise/A+ spec. Some notable differences from other promise libaries are: * - There currently isn't a seperate deferred and promise object. * - Unhandled rejections eventually show an error if they aren't handled. * * Based off of the work in: * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 */ var Promise = PDFJS.Promise = (function PromiseClosure() { var STATUS_PENDING = 0; var STATUS_RESOLVED = 1; var STATUS_REJECTED = 2; // In an attempt to avoid silent exceptions, unhandled rejections are // tracked and if they aren't handled in a certain amount of time an // error is logged. var REJECTION_TIMEOUT = 500; var HandlerManager = { handlers: [], running: false, unhandledRejections: [], pendingRejectionCheck: false, scheduleHandlers: function scheduleHandlers(promise) { if (promise._status == STATUS_PENDING) { return; } this.handlers = this.handlers.concat(promise._handlers); promise._handlers = []; if (this.running) { return; } this.running = true; setTimeout(this.runHandlers.bind(this), 0); }, runHandlers: function runHandlers() { while (this.handlers.length > 0) { var handler = this.handlers.shift(); var nextStatus = handler.thisPromise._status; var nextValue = handler.thisPromise._value; try { if (nextStatus === STATUS_RESOLVED) { if (typeof(handler.onResolve) == 'function') { nextValue = handler.onResolve(nextValue); } } else if (typeof(handler.onReject) === 'function') { nextValue = handler.onReject(nextValue); nextStatus = STATUS_RESOLVED; if (handler.thisPromise._unhandledRejection) { this.removeUnhandeledRejection(handler.thisPromise); } } } catch (ex) { nextStatus = STATUS_REJECTED; nextValue = ex; } handler.nextPromise._updateStatus(nextStatus, nextValue); } this.running = false; }, addUnhandledRejection: function addUnhandledRejection(promise) { this.unhandledRejections.push({ promise: promise, time: Date.now() }); this.scheduleRejectionCheck(); }, removeUnhandeledRejection: function removeUnhandeledRejection(promise) { promise._unhandledRejection = false; for (var i = 0; i < this.unhandledRejections.length; i++) { if (this.unhandledRejections[i].promise === promise) { this.unhandledRejections.splice(i); i--; } } }, scheduleRejectionCheck: function scheduleRejectionCheck() { if (this.pendingRejectionCheck) { return; } this.pendingRejectionCheck = true; setTimeout(function rejectionCheck() { this.pendingRejectionCheck = false; var now = Date.now(); for (var i = 0; i < this.unhandledRejections.length; i++) { if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { var unhandled = this.unhandledRejections[i].promise._value; var msg = 'Unhandled rejection: ' + unhandled; if (unhandled.stack) { msg += '\n' + unhandled.stack; } warn(msg); this.unhandledRejections.splice(i); i--; } } if (this.unhandledRejections.length) { this.scheduleRejectionCheck(); } }.bind(this), REJECTION_TIMEOUT); } }; function Promise() { this._status = STATUS_PENDING; this._handlers = []; } /** * Builds a promise that is resolved when all the passed in promises are * resolved. * @param {array} array of data and/or 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; } function reject(reason) { if (deferred._status === STATUS_REJECTED) { return; } results = []; deferred.reject(reason); } for (var i = 0, ii = promises.length; i < ii; ++i) { var promise = promises[i]; var resolve = (function(i) { return function(value) { if (deferred._status === STATUS_REJECTED) { return; } results[i] = value; unresolved--; if (unresolved === 0) deferred.resolve(results); }; })(i); if (Promise.isPromise(promise)) { promise.then(resolve, reject); } else { resolve(promise); } } return deferred; }; /** * Checks if the value is likely a promise (has a 'then' function). * @return {boolean} true if x is thenable */ Promise.isPromise = function Promise_isPromise(value) { return value && typeof value.then === 'function'; }; Promise.prototype = { _status: null, _value: null, _handlers: null, _unhandledRejection: null, _updateStatus: function Promise__updateStatus(status, value) { if (this._status === STATUS_RESOLVED || this._status === STATUS_REJECTED) { return; } if (status == STATUS_RESOLVED && Promise.isPromise(value)) { value.then(this._updateStatus.bind(this, STATUS_RESOLVED), this._updateStatus.bind(this, STATUS_REJECTED)); return; } this._status = status; this._value = value; if (status === STATUS_REJECTED && this._handlers.length === 0) { this._unhandledRejection = true; HandlerManager.addUnhandledRejection(this); } HandlerManager.scheduleHandlers(this); }, get isResolved() { return this._status === STATUS_RESOLVED; }, get isRejected() { return this._status === STATUS_REJECTED; }, resolve: function Promise_resolve(value) { this._updateStatus(STATUS_RESOLVED, value); }, reject: function Promise_reject(reason) { this._updateStatus(STATUS_REJECTED, reason); }, then: function Promise_then(onResolve, onReject) { var nextPromise = new Promise(); this._handlers.push({ thisPromise: this, onResolve: onResolve, onReject: onReject, nextPromise: nextPromise }); HandlerManager.scheduleHandlers(this); return nextPromise; } }; 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) warn('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)) warn('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; })(); PDFJS.createBlob = function createBlob(data, contentType) { if (typeof Blob !== 'undefined') return new Blob([data], { type: contentType }); // Blob builder is deprecated in FF14 and removed in FF18. var bb = new MozBlobBuilder(); bb.append(data); return bb.getBlob(contentType); }; PDFJS.createObjectURL = (function createObjectURLClosure() { if (typeof URL !== 'undefined' && URL.createObjectURL) { return function createObjectURL(data, contentType) { var blob = PDFJS.createBlob(data, contentType); return URL.createObjectURL(blob); }; } // Blob/createObjectURL is not available, falling back to data schema. var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; return function createObjectURL(data, contentType) { var buffer = 'data:' + contentType + ';base64,'; for (var i = 0, ii = data.length; i < ii; i += 3) { var b1 = data[i] & 0xFF; var b2 = data[i + 1] & 0xFF; var b3 = data[i + 2] & 0xFF; var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; var d4 = i + 2 < ii ? (b3 & 0x3F) : 64; buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4]; } return buffer; }; })(); function MessageHandler(name, comObj) { this.name = name; this.comObj = comObj; this.callbackIndex = 1; this.postMessageTransfers = true; var callbacks = this.callbacks = {}; var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { log.apply(null, data); }]; // If there's no console available, console_error in the // action handler will do nothing. if ('console' in globalScope) { ah['console_error'] = [function ahConsoleError(data) { globalScope['console'].error.apply(null, data); }]; } else { ah['console_error'] = [function ahConsoleError(data) { log.apply(null, data); }]; } ah['_warn'] = [function ah_Warn(data) { warn(data); }]; comObj.onmessage = function messageHandlerComObjOnMessage(event) { var data = event.data; if (data.isReply) { var callbackId = data.callbackId; if (data.callbackId in callbacks) { var callback = callbacks[callbackId]; delete callbacks[callbackId]; callback(data.data); } else { error('Cannot resolve callback ' + callbackId); } } else if (data.action in ah) { var action = ah[data.action]; if (data.callbackId) { var promise = new Promise(); promise.then(function(resolvedData) { comObj.postMessage({ isReply: true, callbackId: data.callbackId, data: resolvedData }); }); action[0].call(action[1], data.data, promise); } else { action[0].call(action[1], data.data); } } else { error('Unkown action from worker: ' + data.action); } }; } MessageHandler.prototype = { on: function messageHandlerOn(actionName, handler, scope) { var ah = this.actionHandler; if (ah[actionName]) { error('There is already an actionName called "' + actionName + '"'); } ah[actionName] = [handler, scope]; }, /** * Sends a message to the comObj to invoke the action with the supplied data. * @param {String} actionName Action to call. * @param {JSON} data JSON data to send. * @param {function} [callback] Optional callback that will handle a reply. * @param {Array} [transfers] Optional list of transfers/ArrayBuffers */ send: function messageHandlerSend(actionName, data, callback, transfers) { var message = { action: actionName, data: data }; if (callback) { var callbackId = this.callbackIndex++; this.callbacks[callbackId] = callback; message.callbackId = callbackId; } if (transfers && this.postMessageTransfers) { this.comObj.postMessage(message, transfers); } else { this.comObj.postMessage(message); } } }; function loadJpegStream(id, imageUrl, objs) { var img = new Image(); img.onload = (function loadJpegStream_onloadClosure() { objs.resolve(id, img); }); img.src = imageUrl; } var ColorSpace = (function ColorSpaceClosure() { // Constructor should define this.numComps, this.defaultColor, this.name function ColorSpace() { error('should not call ColorSpace constructor'); } ColorSpace.prototype = { /** * Converts the color value to the RGB color. The color components are * located in the src array starting from the srcOffset. Returns the array * of the rgb components, each value ranging from [0,255]. */ getRgb: function ColorSpace_getRgb(src, srcOffset) { error('Should not call ColorSpace.getRgb'); }, /** * Converts the color value to the RGB color, similar to the getRgb method. * The result placed into the dest array starting from the destOffset. */ getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) { error('Should not call ColorSpace.getRgbItem'); }, /** * Converts the specified number of the color values to the RGB colors. * The colors are located in the src array starting from the srcOffset. * The result is placed into the dest array starting from the destOffset. * The src array items shall be in [0,2^bits) range, the dest array items * will be in [0,255] range. */ getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { error('Should not call ColorSpace.getRgbBuffer'); }, /** * Determines amount of the bytes is required to store the reslut of the * conversion that done by the getRgbBuffer method. */ getOutputLength: function ColorSpace_getOutputLength(inputLength) { error('Should not call ColorSpace.getOutputLength'); }, /** * Returns true if source data will be equal the result/output data. */ isPassthrough: function ColorSpace_isPassthrough(bits) { return false; }, /** * Creates the output buffer and converts the specified number of the color * values to the RGB colors, similar to the getRgbBuffer. */ createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset, count, bits) { if (this.isPassthrough(bits)) { return src.subarray(srcOffset); } var dest = new Uint8Array(count * 3); var numComponentColors = 1 << bits; // Optimization: create a color map when there is just one component and // we are converting more colors than the size of the color map. We // don't build the map if the colorspace is gray or rgb since those // methods are faster than building a map. This mainly offers big speed // ups for indexed and alternate colorspaces. if (this.numComps === 1 && count > numComponentColors && this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') { // TODO it may be worth while to cache the color map. While running // testing I never hit a cache so I will leave that out for now (perhaps // we are reparsing colorspaces too much?). var allColors = bits <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors); for (var i = 0; i < numComponentColors; i++) { allColors[i] = i; } var colorMap = new Uint8Array(numComponentColors * 3); this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits); var destOffset = 0; for (var i = 0; i < count; ++i) { var key = src[srcOffset++] * 3; dest[destOffset++] = colorMap[key]; dest[destOffset++] = colorMap[key + 1]; dest[destOffset++] = colorMap[key + 2]; } return dest; } this.getRgbBuffer(src, srcOffset, count, dest, 0, bits); return dest; }, /** * True if the colorspace has components in the default range of [0, 1]. * This should be true for all colorspaces except for lab color spaces * which are [0,100], [-128, 127], [-128, 127]. */ usesZeroToOneRange: true }; ColorSpace.parse = function ColorSpace_parse(cs, xref, res) { var IR = ColorSpace.parseToIR(cs, xref, res); if (IR instanceof AlternateCS) return IR; return ColorSpace.fromIR(IR); }; ColorSpace.fromIR = function ColorSpace_fromIR(IR) { var name = isArray(IR) ? IR[0] : IR; switch (name) { case 'DeviceGrayCS': return this.singletons.gray; case 'DeviceRgbCS': return this.singletons.rgb; case 'DeviceCmykCS': return this.singletons.cmyk; case 'CalGrayCS': var whitePoint = IR[1].WhitePoint; var blackPoint = IR[1].BlackPoint; var gamma = IR[1].Gamma; return new CalGrayCS(whitePoint, blackPoint, gamma); case 'PatternCS': var basePatternCS = IR[1]; if (basePatternCS) basePatternCS = ColorSpace.fromIR(basePatternCS); return new PatternCS(basePatternCS); case 'IndexedCS': var baseIndexedCS = IR[1]; var hiVal = IR[2]; var lookup = IR[3]; return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup); case 'AlternateCS': var numComps = IR[1]; var alt = IR[2]; var tintFnIR = IR[3]; return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); case 'LabCS': var whitePoint = IR[1].WhitePoint; var blackPoint = IR[1].BlackPoint; var range = IR[1].Range; return new LabCS(whitePoint, blackPoint, range); default: error('Unkown name ' + name); } return null; }; ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) { if (isName(cs)) { var colorSpaces = res.get('ColorSpace'); if (isDict(colorSpaces)) { var refcs = colorSpaces.get(cs.name); if (refcs) cs = refcs; } } cs = xref.fetchIfRef(cs); var mode; if (isName(cs)) { mode = cs.name; this.mode = mode; switch (mode) { case 'DeviceGray': case 'G': return 'DeviceGrayCS'; case 'DeviceRGB': case 'RGB': return 'DeviceRgbCS'; case 'DeviceCMYK': case 'CMYK': return 'DeviceCmykCS'; case 'Pattern': return ['PatternCS', null]; default: error('unrecognized colorspace ' + mode); } } else if (isArray(cs)) { mode = cs[0].name; this.mode = mode; switch (mode) { case 'DeviceGray': case 'G': return 'DeviceGrayCS'; case 'DeviceRGB': case 'RGB': return 'DeviceRgbCS'; case 'DeviceCMYK': case 'CMYK': return 'DeviceCmykCS'; case 'CalGray': var params = cs[1].getAll(); return ['CalGrayCS', params]; case 'CalRGB': return 'DeviceRgbCS'; case 'ICCBased': var stream = xref.fetchIfRef(cs[1]); var dict = stream.dict; var numComps = dict.get('N'); if (numComps == 1) return 'DeviceGrayCS'; if (numComps == 3) return 'DeviceRgbCS'; if (numComps == 4) return 'DeviceCmykCS'; break; case 'Pattern': var basePatternCS = cs[1]; if (basePatternCS) basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res); return ['PatternCS', basePatternCS]; case 'Indexed': case 'I': var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res); var hiVal = cs[2] + 1; var lookup = xref.fetchIfRef(cs[3]); if (isStream(lookup)) { lookup = lookup.getBytes(); } return ['IndexedCS', baseIndexedCS, hiVal, lookup]; case 'Separation': case 'DeviceN': var name = cs[1]; var numComps = 1; if (isName(name)) numComps = 1; else if (isArray(name)) numComps = name.length; var alt = ColorSpace.parseToIR(cs[2], xref, res); var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': var params = cs[1].getAll(); return ['LabCS', params]; default: error('unimplemented color space object "' + mode + '"'); } } else { error('unrecognized color space object: "' + cs + '"'); } return null; }; /** * Checks if a decode map matches the default decode map for a color space. * This handles the general decode maps where there are two values per * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color. * This does not handle Lab, Indexed, or Pattern decode maps since they are * slightly different. * @param {Array} decode Decode map (usually from an image). * @param {Number} n Number of components the color space has. */ ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) { if (!decode) return true; if (n * 2 !== decode.length) { warn('The decode map is not the correct length'); return true; } for (var i = 0, ii = decode.length; i < ii; i += 2) { if (decode[i] !== 0 || decode[i + 1] != 1) return false; } return true; }; ColorSpace.singletons = { get gray() { return shadow(this, 'gray', new DeviceGrayCS()); }, get rgb() { return shadow(this, 'rgb', new DeviceRgbCS()); }, get cmyk() { return shadow(this, 'cmyk', new DeviceCmykCS()); } }; return ColorSpace; })(); /** * Alternate color space handles both Separation and DeviceN color spaces. A * Separation color space is actually just a DeviceN with one color component. * Both color spaces use a tinting function to convert colors to a base color * space. */ var AlternateCS = (function AlternateCSClosure() { function AlternateCS(numComps, base, tintFn) { this.name = 'Alternate'; this.numComps = numComps; this.defaultColor = new Float32Array(numComps); for (var i = 0; i < numComps; ++i) { this.defaultColor[i] = 1; } this.base = base; this.tintFn = tintFn; } AlternateCS.prototype = { getRgb: function AlternateCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); this.getRgbItem(src, srcOffset, rgb, 0); return rgb; }, getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, dest, destOffset) { var baseNumComps = this.base.numComps; var input = 'subarray' in src ? src.subarray(srcOffset, srcOffset + this.numComps) : Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps); var tinted = this.tintFn(input); this.base.getRgbItem(tinted, 0, dest, destOffset); }, getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var tintFn = this.tintFn; var base = this.base; var scale = 1 / ((1 << bits) - 1); var baseNumComps = base.numComps; var usesZeroToOneRange = base.usesZeroToOneRange; var isPassthrough = base.isPassthrough(8) || !usesZeroToOneRange; var pos = isPassthrough ? destOffset : 0; var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count); var numComps = this.numComps; var scaled = new Float32Array(numComps); for (var i = 0; i < count; i++) { for (var j = 0; j < numComps; j++) { scaled[j] = src[srcOffset++] * scale; } var tinted = tintFn(scaled); if (usesZeroToOneRange) { for (var j = 0; j < baseNumComps; j++) { baseBuf[pos++] = tinted[j] * 255; } } else { base.getRgbItem(tinted, 0, baseBuf, pos); pos += baseNumComps; } } if (!isPassthrough) { base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8); } }, getOutputLength: function AlternateCS_getOutputLength(inputLength) { return this.base.getOutputLength(inputLength * this.base.numComps / this.numComps); }, isPassthrough: ColorSpace.prototype.isPassthrough, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, usesZeroToOneRange: true }; return AlternateCS; })(); var PatternCS = (function PatternCSClosure() { function PatternCS(baseCS) { this.name = 'Pattern'; this.base = baseCS; } PatternCS.prototype = {}; return PatternCS; })(); var IndexedCS = (function IndexedCSClosure() { function IndexedCS(base, highVal, lookup) { this.name = 'Indexed'; this.numComps = 1; this.defaultColor = new Uint8Array([0]); this.base = base; this.highVal = highVal; var baseNumComps = base.numComps; var length = baseNumComps * highVal; var lookupArray; if (isStream(lookup)) { lookupArray = new Uint8Array(length); var bytes = lookup.getBytes(length); lookupArray.set(bytes); } else if (isString(lookup)) { lookupArray = new Uint8Array(length); for (var i = 0; i < length; ++i) lookupArray[i] = lookup.charCodeAt(i); } else if (lookup instanceof Uint8Array || lookup instanceof Array) { lookupArray = lookup; } else { error('Unrecognized lookup table: ' + lookup); } this.lookup = lookupArray; } IndexedCS.prototype = { getRgb: function IndexedCS_getRgb(src, srcOffset) { var numComps = this.base.numComps; var start = src[srcOffset] * numComps; return this.base.getRgb(this.lookup, start); }, getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, dest, destOffset) { var numComps = this.base.numComps; var start = src[srcOffset] * numComps; this.base.getRgbItem(this.lookup, start, dest, destOffset); }, getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, dest, destOffset) { var base = this.base; var numComps = base.numComps; var outputDelta = base.getOutputLength(numComps); var lookup = this.lookup; for (var i = 0; i < count; ++i) { var lookupPos = src[srcOffset++] * numComps; base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8); destOffset += outputDelta; } }, getOutputLength: function IndexedCS_getOutputLength(inputLength) { return this.base.getOutputLength(inputLength * this.base.numComps); }, isPassthrough: ColorSpace.prototype.isPassthrough, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) { // indexed color maps shouldn't be changed return true; }, usesZeroToOneRange: true }; return IndexedCS; })(); var DeviceGrayCS = (function DeviceGrayCSClosure() { function DeviceGrayCS() { this.name = 'DeviceGray'; this.numComps = 1; this.defaultColor = new Float32Array([0]); } DeviceGrayCS.prototype = { getRgb: function DeviceGrayCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); this.getRgbItem(src, srcOffset, rgb, 0); return rgb; }, getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, dest, destOffset) { var c = (src[srcOffset] * 255) | 0; c = c < 0 ? 0 : c > 255 ? 255 : c; dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; }, getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var scale = 255 / ((1 << bits) - 1); var j = srcOffset, q = destOffset; for (var i = 0; i < count; ++i) { var c = (scale * src[j++]) | 0; dest[q++] = c; dest[q++] = c; dest[q++] = c; } }, getOutputLength: function DeviceGrayCS_getOutputLength(inputLength) { return inputLength * 3; }, isPassthrough: ColorSpace.prototype.isPassthrough, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, usesZeroToOneRange: true }; return DeviceGrayCS; })(); var DeviceRgbCS = (function DeviceRgbCSClosure() { function DeviceRgbCS() { this.name = 'DeviceRGB'; this.numComps = 3; this.defaultColor = new Float32Array([0, 0, 0]); } DeviceRgbCS.prototype = { getRgb: function DeviceRgbCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); this.getRgbItem(src, srcOffset, rgb, 0); return rgb; }, getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, dest, destOffset) { var r = (src[srcOffset] * 255) | 0; var g = (src[srcOffset + 1] * 255) | 0; var b = (src[srcOffset + 2] * 255) | 0; dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r; dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g; dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b; }, getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var length = count * 3; if (bits == 8) { dest.set(src.subarray(srcOffset, srcOffset + length), destOffset); return; } var scale = 255 / ((1 << bits) - 1); var j = srcOffset, q = destOffset; for (var i = 0; i < length; ++i) { dest[q++] = (scale * src[j++]) | 0; } }, getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) { return inputLength; }, isPassthrough: function DeviceRgbCS_isPassthrough(bits) { return bits == 8; }, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, usesZeroToOneRange: true }; return DeviceRgbCS; })(); var DeviceCmykCS = (function DeviceCmykCSClosure() { // The coefficients below was found using numerical analysis: the method of // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors, // where color_value is the tabular value from the table of sampled RGB colors // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding // CMYK color conversion using the estimation below: // f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255 function convertToRgb(src, srcOffset, srcScale, dest, destOffset) { var c = src[srcOffset + 0] * srcScale; var m = src[srcOffset + 1] * srcScale; var y = src[srcOffset + 2] * srcScale; var k = src[srcOffset + 3] * srcScale; var r = c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k + -285.2331026137004) + m * (1.7149763477362134 * m - 5.6096736904047315 * y + -17.873870861415444 * k - 5.497006427196366) + y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) + k * (-21.86122147463605 * k - 189.48180835922747) + 255; var g = c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k + -79.2970844816548) + m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + k * (-20.737325471181034 * k - 187.80453709719578) + 255; var b = c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k + -14.183576799673286) + m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) + y * (0.03296041114873217 * y + 115.60384449646641 * k + -193.58209356861505) + k * (-22.33816807309886 * k - 180.12613974708367) + 255; dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r; dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g; dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b; } function DeviceCmykCS() { this.name = 'DeviceCMYK'; this.numComps = 4; this.defaultColor = new Float32Array([0, 0, 0, 1]); } DeviceCmykCS.prototype = { getRgb: function DeviceCmykCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); convertToRgb(src, srcOffset, 1, rgb, 0); return rgb; }, getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, dest, destOffset) { convertToRgb(src, srcOffset, 1, dest, destOffset); }, getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var scale = 1 / ((1 << bits) - 1); for (var i = 0; i < count; i++) { convertToRgb(src, srcOffset, scale, dest, destOffset); srcOffset += 4; destOffset += 3; } }, getOutputLength: function DeviceCmykCS_getOutputLength(inputLength) { return (inputLength >> 2) * 3; }, isPassthrough: ColorSpace.prototype.isPassthrough, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, usesZeroToOneRange: true }; return DeviceCmykCS; })(); // // CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245 // var CalGrayCS = (function CalGrayCSClosure() { function CalGrayCS(whitePoint, blackPoint, gamma) { this.name = 'CalGray'; this.numComps = 3; this.defaultColor = new Float32Array([0, 0, 0]); if (!whitePoint) { error('WhitePoint missing - required for color space CalGray'); } blackPoint = blackPoint || [0, 0, 0]; gamma = gamma || 1; // Translate arguments to spec variables. this.XW = whitePoint[0]; this.YW = whitePoint[1]; this.ZW = whitePoint[2]; this.XB = blackPoint[0]; this.YB = blackPoint[1]; this.ZB = blackPoint[2]; this.G = gamma; // Validate variables as per spec. if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) { error('Invalid WhitePoint components for ' + this.name + ', no fallback available'); } if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { info('Invalid BlackPoint for ' + this.name + ', falling back to default'); this.XB = this.YB = this.ZB = 0; } if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) { TODO(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + ', ZB: ' + this.ZB + ', only default values are supported.'); } if (this.G < 1) { info('Invalid Gamma: ' + this.G + ' for ' + this.name + ', falling back to default'); this.G = 1; } } CalGrayCS.prototype = { getRgb: function CalGrayCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); this.getRgbItem(src, srcOffset, rgb, 0); return rgb; }, getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) { // A represents a gray component of a calibrated gray space. // A <---> AG in the spec var A = src[srcOffset]; var AG = Math.pow(A, this.G); // Computes intermediate variables M, L, N as per spec. // Except if other than default BlackPoint values are used. var M = this.XW * AG; var L = this.YW * AG; var N = this.ZW * AG; // Decode XYZ, as per spec. var X = M; var Y = L; var Z = N; // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4. // This yields values in range [0, 100]. var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0); // Convert values to rgb range [0, 255]. dest[destOffset] = Lstar * 255 / 100; dest[destOffset + 1] = Lstar * 255 / 100; dest[destOffset + 2] = Lstar * 255 / 100; }, getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { // TODO: This part is copied from DeviceGray. Make this utility function. var scale = 255 / ((1 << bits) - 1); var j = srcOffset, q = destOffset; for (var i = 0; i < count; ++i) { var c = (scale * src[j++]) | 0; dest[q++] = c; dest[q++] = c; dest[q++] = c; } }, getOutputLength: function CalGrayCS_getOutputLength(inputLength) { return inputLength * 3; }, isPassthrough: ColorSpace.prototype.isPassthrough, createRgbBuffer: ColorSpace.prototype.createRgbBuffer, isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, usesZeroToOneRange: true }; return CalGrayCS; })(); // // LabCS: Based on "PDF Reference, Sixth Ed", p.250 // var LabCS = (function LabCSClosure() { function LabCS(whitePoint, blackPoint, range) { this.name = 'Lab'; this.numComps = 3; this.defaultColor = new Float32Array([0, 0, 0]); if (!whitePoint) error('WhitePoint missing - required for color space Lab'); blackPoint = blackPoint || [0, 0, 0]; range = range || [-100, 100, -100, 100]; // Translate args to spec variables this.XW = whitePoint[0]; this.YW = whitePoint[1]; this.ZW = whitePoint[2]; this.amin = range[0]; this.amax = range[1]; this.bmin = range[2]; this.bmax = range[3]; // These are here just for completeness - the spec doesn't offer any // formulas that use BlackPoint in Lab this.XB = blackPoint[0]; this.YB = blackPoint[1]; this.ZB = blackPoint[2]; // Validate vars as per spec if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) error('Invalid WhitePoint components, no fallback available'); if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { info('Invalid BlackPoint, falling back to default'); this.XB = this.YB = this.ZB = 0; } if (this.amin > this.amax || this.bmin > this.bmax) { info('Invalid Range, falling back to defaults'); this.amin = -100; this.amax = 100; this.bmin = -100; this.bmax = 100; } } // Function g(x) from spec function fn_g(x) { if (x >= 6 / 29) return x * x * x; else return (108 / 841) * (x - 4 / 29); } function decode(value, high1, low2, high2) { return low2 + (value) * (high2 - low2) / (high1); } // If decoding is needed maxVal should be 2^bits per component - 1. function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) { // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax] // not the usual [0, 1]. If a command like setFillColor is used the src // values will already be within the correct range. However, if we are // converting an image we have to map the values to the correct range given // above. // Ls,as,bs <---> L*,a*,b* in the spec var Ls = src[srcOffset]; var as = src[srcOffset + 1]; var bs = src[srcOffset + 2]; if (maxVal !== false) { Ls = decode(Ls, maxVal, 0, 100); as = decode(as, maxVal, cs.amin, cs.amax); bs = decode(bs, maxVal, cs.bmin, cs.bmax); } // Adjust limits of 'as' and 'bs' as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as; bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs; // Computes intermediate variables X,Y,Z as per spec var M = (Ls + 16) / 116; var L = M + (as / 500); var N = M - (bs / 200); var X = cs.XW * fn_g(L); var Y = cs.YW * fn_g(M); var Z = cs.ZW * fn_g(N); var r, g, b; // Using different conversions for D50 and D65 white points, // per http://www.color.org/srgb.pdf if (cs.ZW < 1) { // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249) r = X * 3.1339 + Y * -1.6170 + Z * -0.4906; g = X * -0.9785 + Y * 1.9160 + Z * 0.0333; b = X * 0.0720 + Y * -0.2290 + Z * 1.4057; } else { // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888) r = X * 3.2406 + Y * -1.5372 + Z * -0.4986; g = X * -0.9689 + Y * 1.8758 + Z * 0.0415; b = X * 0.0557 + Y * -0.2040 + Z * 1.0570; } // clamp color values to [0,1] range then convert to [0,255] range. dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255; dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255; dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255; } LabCS.prototype = { getRgb: function LabCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); convertToRgb(this, src, srcOffset, false, rgb, 0); return rgb; }, getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) { convertToRgb(this, src, srcOffset, false, dest, destOffset); }, getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, dest, destOffset, bits) { var maxVal = (1 << bits) - 1; for (var i = 0; i < count; i++) { convertToRgb(this, src, srcOffset, maxVal, dest, destOffset); srcOffset += 3; destOffset += 3; } }, getOutputLength: function LabCS_getOutputLength(inputLength) { return inputLength; }, isPassthrough: ColorSpace.prototype.isPassthrough, isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) { // XXX: Decoding is handled with the lab conversion because of the strange // ranges that are used. return true; }, usesZeroToOneRange: false }; return LabCS; })(); var PatternType = { AXIAL: 2, RADIAL: 3 }; var Pattern = (function PatternClosure() { // Constructor should define this.getPattern function Pattern() { error('should not call Pattern constructor'); } Pattern.prototype = { // Input: current Canvas context // Output: the appropriate fillStyle or strokeStyle getPattern: function Pattern_getPattern(ctx) { error('Should not call Pattern.getStyle: ' + ctx); } }; Pattern.shadingFromIR = function Pattern_shadingFromIR(raw) { return Shadings[raw[0]].fromIR(raw); }; Pattern.parseShading = function Pattern_parseShading(shading, matrix, xref, res) { var dict = isStream(shading) ? shading.dict : shading; var type = dict.get('ShadingType'); switch (type) { case PatternType.AXIAL: case PatternType.RADIAL: // Both radial and axial shadings are handled by RadialAxial shading. return new Shadings.RadialAxial(dict, matrix, xref, res); default: TODO('Unsupported shading type: ' + type); return new Shadings.Dummy(); } }; return Pattern; })(); var Shadings = {}; // A small number to offset the first/last color stops so we can insert ones to // support extend. Number.MIN_VALUE appears to be too small and breaks the // extend. 1e-7 works in FF but chrome seems to use an even smaller sized number // internally so we have to go bigger. Shadings.SMALL_NUMBER = 1e-2; // Radial and axial shading have very similar implementations // If needed, the implementations can be broken into two classes Shadings.RadialAxial = (function RadialAxialClosure() { function RadialAxial(dict, matrix, xref, res, ctx) { this.matrix = matrix; this.coordsArr = dict.get('Coords'); this.shadingType = dict.get('ShadingType'); this.type = 'Pattern'; this.ctx = ctx; var cs = dict.get('ColorSpace', 'CS'); cs = ColorSpace.parse(cs, xref, res); this.cs = cs; var t0 = 0.0, t1 = 1.0; if (dict.has('Domain')) { var domainArr = dict.get('Domain'); t0 = domainArr[0]; t1 = domainArr[1]; } var extendStart = false, extendEnd = false; if (dict.has('Extend')) { var extendArr = dict.get('Extend'); extendStart = extendArr[0]; extendEnd = extendArr[1]; } if (this.shadingType === PatternType.RADIAL && (!extendStart || !extendEnd)) { // Radial gradient only currently works if either circle is fully within // the other circle. var x1 = this.coordsArr[0]; var y1 = this.coordsArr[1]; var r1 = this.coordsArr[2]; var x2 = this.coordsArr[3]; var y2 = this.coordsArr[4]; var r2 = this.coordsArr[5]; var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); if (r1 <= r2 + distance && r2 <= r1 + distance) { warn('Unsupported radial gradient.'); } } this.extendStart = extendStart; this.extendEnd = extendEnd; var fnObj = dict.get('Function'); var fn; if (isArray(fnObj)) { var fnArray = []; for (var j = 0, jj = fnObj.length; j < jj; j++) { var obj = xref.fetchIfRef(fnObj[j]); if (!isPDFFunction(obj)) { error('Invalid function'); } fnArray.push(PDFFunction.parse(xref, obj)); } fn = function radialAxialColorFunction(arg) { var out = []; for (var i = 0, ii = fnArray.length; i < ii; i++) { out.push(fnArray[i](arg)[0]); } return out; }; } else { if (!isPDFFunction(fnObj)) { error('Invalid function'); } fn = PDFFunction.parse(xref, fnObj); } // 10 samples seems good enough for now, but probably won't work // if there are sharp color changes. Ideally, we would implement // the spec faithfully and add lossless optimizations. var diff = t1 - t0; var step = diff / 10; var colorStops = this.colorStops = []; // Protect against bad domains so we don't end up in an infinte loop below. if (t0 >= t1 || step <= 0) { // Acrobat doesn't seem to handle these cases so we'll ignore for // now. info('Bad shading domain.'); return; } for (var i = t0; i <= t1; i += step) { var rgbColor = cs.getRgb(fn([i]), 0); var cssColor = Util.makeCssRgb(rgbColor); colorStops.push([(i - t0) / diff, cssColor]); } var background = 'transparent'; if (dict.has('Background')) { var rgbColor = cs.getRgb(dict.get('Background'), 0); background = Util.makeCssRgb(rgbColor); } if (!extendStart) { // Insert a color stop at the front and offset the first real color stop // so it doesn't conflict with the one we insert. colorStops.unshift([0, background]); colorStops[1][0] += Shadings.SMALL_NUMBER; } if (!extendEnd) { // Same idea as above in extendStart but for the end. colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER; colorStops.push([1, background]); } this.colorStops = colorStops; } RadialAxial.fromIR = function RadialAxial_fromIR(raw) { var type = raw[1]; var colorStops = raw[2]; var p0 = raw[3]; var p1 = raw[4]; var r0 = raw[5]; var r1 = raw[6]; return { type: 'Pattern', getPattern: function RadialAxial_getPattern(ctx) { var grad; if (type == PatternType.AXIAL) grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]); else if (type == PatternType.RADIAL) grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1); for (var i = 0, ii = colorStops.length; i < ii; ++i) { var c = colorStops[i]; grad.addColorStop(c[0], c[1]); } return grad; } }; }; RadialAxial.prototype = { getIR: function RadialAxial_getIR() { var coordsArr = this.coordsArr; var type = this.shadingType; if (type == PatternType.AXIAL) { var p0 = [coordsArr[0], coordsArr[1]]; var p1 = [coordsArr[2], coordsArr[3]]; var r0 = null; var r1 = null; } else if (type == PatternType.RADIAL) { var p0 = [coordsArr[0], coordsArr[1]]; var p1 = [coordsArr[3], coordsArr[4]]; var r0 = coordsArr[2]; var r1 = coordsArr[5]; } else { error('getPattern type unknown: ' + type); } var matrix = this.matrix; if (matrix) { p0 = Util.applyTransform(p0, matrix); p1 = Util.applyTransform(p1, matrix); } return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1]; } }; return RadialAxial; })(); Shadings.Dummy = (function DummyClosure() { function Dummy() { this.type = 'Pattern'; } Dummy.fromIR = function Dummy_fromIR() { return { type: 'Pattern', getPattern: function Dummy_fromIR_getPattern() { return 'hotpink'; } }; }; Dummy.prototype = { getIR: function Dummy_getIR() { return ['Dummy']; } }; return Dummy; })(); var TilingPattern = (function TilingPatternClosure() { var PaintType = { COLORED: 1, UNCOLORED: 2 }; var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough function TilingPattern(IR, color, ctx, objs, commonObjs, baseTransform) { this.name = IR[1][0].name; this.operatorList = IR[2]; this.matrix = IR[3] || [1, 0, 0, 1, 0, 0]; this.bbox = IR[4]; this.xstep = IR[5]; this.ystep = IR[6]; this.paintType = IR[7]; this.tilingType = IR[8]; this.color = color; this.objs = objs; this.commonObjs = commonObjs; this.baseTransform = baseTransform; this.type = 'Pattern'; this.ctx = ctx; } TilingPattern.getIR = function TilingPattern_getIR(operatorList, dict, args) { var matrix = dict.get('Matrix'); var bbox = dict.get('BBox'); var xstep = dict.get('XStep'); var ystep = dict.get('YStep'); var paintType = dict.get('PaintType'); var tilingType = dict.get('TilingType'); return [ 'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType, tilingType ]; }; TilingPattern.prototype = { createPatternCanvas: function TilinPattern_createPatternCanvas(owner) { var operatorList = this.operatorList; var bbox = this.bbox; var xstep = this.xstep; var ystep = this.ystep; var paintType = this.paintType; var tilingType = this.tilingType; var color = this.color; var objs = this.objs; var commonObjs = this.commonObjs; var ctx = this.ctx; TODO('TilingType: ' + tilingType); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; var topLeft = [x0, y0]; // we want the canvas to be as large as the step size var botRight = [x0 + xstep, y0 + ystep]; var width = botRight[0] - topLeft[0]; var height = botRight[1] - topLeft[1]; // Obtain scale from matrix and current transformation matrix. var matrixScale = Util.singularValueDecompose2dScale(this.matrix); var curMatrixScale = Util.singularValueDecompose2dScale( this.baseTransform); var combinedScale = [matrixScale[0] * curMatrixScale[0], matrixScale[1] * curMatrixScale[1]]; // MAX_PATTERN_SIZE is used to avoid OOM situation. // Use width and height values that are as close as possible to the end // result when the pattern is used. Too low value makes the pattern look // blurry. Too large value makes it look too crispy. width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])), MAX_PATTERN_SIZE); height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])), MAX_PATTERN_SIZE); var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true); var tmpCtx = tmpCanvas.context; var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs); graphics.groupLevel = owner.groupLevel; this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color); this.setScale(width, height, xstep, ystep); this.transformToScale(graphics); // transform coordinates to pattern space var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]]; graphics.transform.apply(graphics, tmpTranslate); this.clipBbox(graphics, bbox, x0, y0, x1, y1); graphics.executeOperatorList(operatorList); return tmpCanvas.canvas; }, setScale: function TilingPattern_setScale(width, height, xstep, ystep) { this.scale = [width / xstep, height / ystep]; }, transformToScale: function TilingPattern_transformToScale(graphics) { var scale = this.scale; var tmpScale = [scale[0], 0, 0, scale[1], 0, 0]; graphics.transform.apply(graphics, tmpScale); }, scaleToContext: function TilingPattern_scaleToContext() { var scale = this.scale; this.ctx.scale(1 / scale[0], 1 / scale[1]); }, clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) { if (bbox && isArray(bbox) && 4 == bbox.length) { var bboxWidth = x1 - x0; var bboxHeight = y1 - y0; graphics.rectangle(x0, y0, bboxWidth, bboxHeight); graphics.clip(); graphics.endPath(); } }, setFillAndStrokeStyleToContext: function setFillAndStrokeStyleToContext(context, paintType, color) { switch (paintType) { case PaintType.COLORED: var ctx = this.ctx; context.fillStyle = ctx.fillStyle; context.strokeStyle = ctx.strokeStyle; break; case PaintType.UNCOLORED: var rgbColor = ColorSpace.singletons.rgb.getRgb(color, 0); var cssColor = Util.makeCssRgb(rgbColor); context.fillStyle = cssColor; context.strokeStyle = cssColor; break; default: error('Unsupported paint type: ' + paintType); } }, getPattern: function TilingPattern_getPattern(ctx, owner) { var temporaryPatternCanvas = this.createPatternCanvas(owner); var ctx = this.ctx; ctx.setTransform.apply(ctx, this.baseTransform); ctx.transform.apply(ctx, this.matrix); this.scaleToContext(); return ctx.createPattern(temporaryPatternCanvas, 'repeat'); } }; return TilingPattern; })(); var PDFFunction = (function PDFFunctionClosure() { var CONSTRUCT_SAMPLED = 0; var CONSTRUCT_INTERPOLATED = 2; var CONSTRUCT_STICHED = 3; var CONSTRUCT_POSTSCRIPT = 4; return { getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps, str) { var length = 1; for (var i = 0, ii = size.length; i < ii; i++) length *= size[i]; length *= outputSize; var array = []; var codeSize = 0; var codeBuf = 0; // 32 is a valid bps so shifting won't work var sampleMul = 1.0 / (Math.pow(2.0, bps) - 1); var strBytes = str.getBytes((length * bps + 7) / 8); var strIdx = 0; for (var i = 0; i < length; i++) { while (codeSize < bps) { codeBuf <<= 8; codeBuf |= strBytes[strIdx++]; codeSize += 8; } codeSize -= bps; array.push((codeBuf >> codeSize) * sampleMul); codeBuf &= (1 << codeSize) - 1; } return array; }, getIR: function PDFFunction_getIR(xref, fn) { var dict = fn.dict; if (!dict) dict = fn; var types = [this.constructSampled, null, this.constructInterpolated, this.constructStiched, this.constructPostScript]; var typeNum = dict.get('FunctionType'); var typeFn = types[typeNum]; if (!typeFn) error('Unknown type of function'); return typeFn.call(this, fn, dict, xref); }, fromIR: function PDFFunction_fromIR(IR) { var type = IR[0]; switch (type) { case CONSTRUCT_SAMPLED: return this.constructSampledFromIR(IR); case CONSTRUCT_INTERPOLATED: return this.constructInterpolatedFromIR(IR); case CONSTRUCT_STICHED: return this.constructStichedFromIR(IR); //case CONSTRUCT_POSTSCRIPT: default: return this.constructPostScriptFromIR(IR); } }, parse: function PDFFunction_parse(xref, fn) { var IR = this.getIR(xref, fn); return this.fromIR(IR); }, constructSampled: function PDFFunction_constructSampled(str, dict) { function toMultiArray(arr) { var inputLength = arr.length; var outputLength = arr.length / 2; var out = []; var index = 0; for (var i = 0; i < inputLength; i += 2) { out[index] = [arr[i], arr[i + 1]]; ++index; } return out; } var domain = dict.get('Domain'); var range = dict.get('Range'); if (!domain || !range) error('No domain or range'); var inputSize = domain.length / 2; var outputSize = range.length / 2; domain = toMultiArray(domain); range = toMultiArray(range); var size = dict.get('Size'); var bps = dict.get('BitsPerSample'); var order = dict.get('Order') || 1; if (order !== 1) { // No description how cubic spline interpolation works in PDF32000:2008 // As in poppler, ignoring order, linear interpolation may work as good TODO('No support for cubic spline interpolation: ' + order); } var encode = dict.get('Encode'); if (!encode) { encode = []; for (var i = 0; i < inputSize; ++i) { encode.push(0); encode.push(size[i] - 1); } } encode = toMultiArray(encode); var decode = dict.get('Decode'); if (!decode) decode = range; else decode = toMultiArray(decode); var samples = this.getSampleArray(size, outputSize, bps, str); return [ CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, outputSize, Math.pow(2, bps) - 1, range ]; }, constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) { // See chapter 3, page 109 of the PDF reference function interpolate(x, xmin, xmax, ymin, ymax) { return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin))); } return function constructSampledFromIRResult(args) { // See chapter 3, page 110 of the PDF reference. var m = IR[1]; var domain = IR[2]; var encode = IR[3]; var decode = IR[4]; var samples = IR[5]; var size = IR[6]; var n = IR[7]; var mask = IR[8]; var range = IR[9]; if (m != args.length) error('Incorrect number of arguments: ' + m + ' != ' + args.length); var x = args; // Building the cube vertices: its part and sample index // http://rjwagner49.com/Mathematics/Interpolation.pdf var cubeVertices = 1 << m; var cubeN = new Float64Array(cubeVertices); var cubeVertex = new Uint32Array(cubeVertices); for (var j = 0; j < cubeVertices; j++) cubeN[j] = 1; var k = n, pos = 1; // Map x_i to y_j for 0 <= i < m using the sampled function. for (var i = 0; i < m; ++i) { // x_i' = min(max(x_i, Domain_2i), Domain_2i+1) var domain_2i = domain[i][0]; var domain_2i_1 = domain[i][1]; var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1); // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1, // Encode_2i, Encode_2i+1) var e = interpolate(xi, domain_2i, domain_2i_1, encode[i][0], encode[i][1]); // e_i' = min(max(e_i, 0), Size_i - 1) var size_i = size[i]; e = Math.min(Math.max(e, 0), size_i - 1); // Adjusting the cube: N and vertex sample index var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1; var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0); var n1 = e - e0; // (e - e0) / (e1 - e0); var offset0 = e0 * k; var offset1 = offset0 + k; // e1 * k for (var j = 0; j < cubeVertices; j++) { if (j & pos) { cubeN[j] *= n1; cubeVertex[j] += offset1; } else { cubeN[j] *= n0; cubeVertex[j] += offset0; } } k *= size_i; pos <<= 1; } var y = new Float64Array(n); for (var j = 0; j < n; ++j) { // Sum all cube vertices' samples portions var rj = 0; for (var i = 0; i < cubeVertices; i++) rj += samples[cubeVertex[i] + j] * cubeN[i]; // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1, // Decode_2j, Decode_2j+1) rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]); // y_j = min(max(r_j, range_2j), range_2j+1) y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]); } return y; }; }, constructInterpolated: function PDFFunction_constructInterpolated(str, dict) { var c0 = dict.get('C0') || [0]; var c1 = dict.get('C1') || [1]; var n = dict.get('N'); if (!isArray(c0) || !isArray(c1)) error('Illegal dictionary for interpolated function'); var length = c0.length; var diff = []; for (var i = 0; i < length; ++i) diff.push(c1[i] - c0[i]); return [CONSTRUCT_INTERPOLATED, c0, diff, n]; }, constructInterpolatedFromIR: function PDFFunction_constructInterpolatedFromIR(IR) { var c0 = IR[1]; var diff = IR[2]; var n = IR[3]; var length = diff.length; return function constructInterpolatedFromIRResult(args) { var x = n == 1 ? args[0] : Math.pow(args[0], n); var out = []; for (var j = 0; j < length; ++j) out.push(c0[j] + (x * diff[j])); return out; }; }, constructStiched: function PDFFunction_constructStiched(fn, dict, xref) { var domain = dict.get('Domain'); if (!domain) error('No domain'); var inputSize = domain.length / 2; if (inputSize != 1) error('Bad domain for stiched function'); var fnRefs = dict.get('Functions'); var fns = []; for (var i = 0, ii = fnRefs.length; i < ii; ++i) fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i]))); var bounds = dict.get('Bounds'); var encode = dict.get('Encode'); return [CONSTRUCT_STICHED, domain, bounds, encode, fns]; }, constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) { var domain = IR[1]; var bounds = IR[2]; var encode = IR[3]; var fnsIR = IR[4]; var fns = []; for (var i = 0, ii = fnsIR.length; i < ii; i++) { fns.push(PDFFunction.fromIR(fnsIR[i])); } return function constructStichedFromIRResult(args) { var clip = function constructStichedFromIRClip(v, min, max) { if (v > max) v = max; else if (v < min) v = min; return v; }; // clip to domain var v = clip(args[0], domain[0], domain[1]); // calulate which bound the value is in for (var i = 0, ii = bounds.length; i < ii; ++i) { if (v < bounds[i]) break; } // encode value into domain of function var dmin = domain[0]; if (i > 0) dmin = bounds[i - 1]; var dmax = domain[1]; if (i < bounds.length) dmax = bounds[i]; var rmin = encode[2 * i]; var rmax = encode[2 * i + 1]; var v2 = rmin + (v - dmin) * (rmax - rmin) / (dmax - dmin); // call the appropropriate function return fns[i]([v2]); }; }, constructPostScript: function PDFFunction_constructPostScript(fn, dict, xref) { var domain = dict.get('Domain'); var range = dict.get('Range'); if (!domain) error('No domain.'); if (!range) error('No range.'); var lexer = new PostScriptLexer(fn); var parser = new PostScriptParser(lexer); var code = parser.parse(); return [CONSTRUCT_POSTSCRIPT, domain, range, code]; }, constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR( IR) { var domain = IR[1]; var range = IR[2]; var code = IR[3]; var numOutputs = range.length / 2; var evaluator = new PostScriptEvaluator(code); // Cache the values for a big speed up, the cache size is limited though // since the number of possible values can be huge from a PS function. var cache = new FunctionCache(); return function constructPostScriptFromIRResult(args) { var initialStack = []; for (var i = 0, ii = (domain.length / 2); i < ii; ++i) { initialStack.push(args[i]); } var key = initialStack.join('_'); if (cache.has(key)) return cache.get(key); var stack = evaluator.execute(initialStack); var transformed = []; for (i = numOutputs - 1; i >= 0; --i) { var out = stack.pop(); var rangeIndex = 2 * i; if (out < range[rangeIndex]) out = range[rangeIndex]; else if (out > range[rangeIndex + 1]) out = range[rangeIndex + 1]; transformed[i] = out; } cache.set(key, transformed); return transformed; }; } }; })(); var FunctionCache = (function FunctionCacheClosure() { // Of 10 PDF's with type4 functions the maxium number of distinct values seen // was 256. This still may need some tweaking in the future though. var MAX_CACHE_SIZE = 1024; function FunctionCache() { this.cache = {}; this.total = 0; } FunctionCache.prototype = { has: function FunctionCache_has(key) { return key in this.cache; }, get: function FunctionCache_get(key) { return this.cache[key]; }, set: function FunctionCache_set(key, value) { if (this.total < MAX_CACHE_SIZE) { this.cache[key] = value; this.total++; } } }; return FunctionCache; })(); var PostScriptStack = (function PostScriptStackClosure() { var MAX_STACK_SIZE = 100; function PostScriptStack(initialStack) { this.stack = initialStack || []; } PostScriptStack.prototype = { push: function PostScriptStack_push(value) { if (this.stack.length >= MAX_STACK_SIZE) error('PostScript function stack overflow.'); this.stack.push(value); }, pop: function PostScriptStack_pop() { if (this.stack.length <= 0) error('PostScript function stack underflow.'); return this.stack.pop(); }, copy: function PostScriptStack_copy(n) { if (this.stack.length + n >= MAX_STACK_SIZE) error('PostScript function stack overflow.'); var stack = this.stack; for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++) stack.push(stack[i]); }, index: function PostScriptStack_index(n) { this.push(this.stack[this.stack.length - n - 1]); }, // rotate the last n stack elements p times roll: function PostScriptStack_roll(n, p) { var stack = this.stack; var l = stack.length - n; var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t; for (i = l, j = r; i < j; i++, j--) { t = stack[i]; stack[i] = stack[j]; stack[j] = t; } for (i = l, j = c - 1; i < j; i++, j--) { t = stack[i]; stack[i] = stack[j]; stack[j] = t; } for (i = c, j = r; i < j; i++, j--) { t = stack[i]; stack[i] = stack[j]; stack[j] = t; } } }; return PostScriptStack; })(); var PostScriptEvaluator = (function PostScriptEvaluatorClosure() { function PostScriptEvaluator(operators, operands) { this.operators = operators; this.operands = operands; } PostScriptEvaluator.prototype = { execute: function PostScriptEvaluator_execute(initialStack) { var stack = new PostScriptStack(initialStack); var counter = 0; var operators = this.operators; var length = operators.length; var operator, a, b; while (counter < length) { operator = operators[counter++]; if (typeof operator == 'number') { // Operator is really an operand and should be pushed to the stack. stack.push(operator); continue; } switch (operator) { // non standard ps operators case 'jz': // jump if false b = stack.pop(); a = stack.pop(); if (!a) counter = b; break; case 'j': // jump a = stack.pop(); counter = a; break; // all ps operators in alphabetical order (excluding if/ifelse) case 'abs': a = stack.pop(); stack.push(Math.abs(a)); break; case 'add': b = stack.pop(); a = stack.pop(); stack.push(a + b); break; case 'and': b = stack.pop(); a = stack.pop(); if (isBool(a) && isBool(b)) stack.push(a && b); else stack.push(a & b); break; case 'atan': a = stack.pop(); stack.push(Math.atan(a)); break; case 'bitshift': b = stack.pop(); a = stack.pop(); if (a > 0) stack.push(a << b); else stack.push(a >> b); break; case 'ceiling': a = stack.pop(); stack.push(Math.ceil(a)); break; case 'copy': a = stack.pop(); stack.copy(a); break; case 'cos': a = stack.pop(); stack.push(Math.cos(a)); break; case 'cvi': a = stack.pop() | 0; stack.push(a); break; case 'cvr': // noop break; case 'div': b = stack.pop(); a = stack.pop(); stack.push(a / b); break; case 'dup': stack.copy(1); break; case 'eq': b = stack.pop(); a = stack.pop(); stack.push(a == b); break; case 'exch': stack.roll(2, 1); break; case 'exp': b = stack.pop(); a = stack.pop(); stack.push(Math.pow(a, b)); break; case 'false': stack.push(false); break; case 'floor': a = stack.pop(); stack.push(Math.floor(a)); break; case 'ge': b = stack.pop(); a = stack.pop(); stack.push(a >= b); break; case 'gt': b = stack.pop(); a = stack.pop(); stack.push(a > b); break; case 'idiv': b = stack.pop(); a = stack.pop(); stack.push((a / b) | 0); break; case 'index': a = stack.pop(); stack.index(a); break; case 'le': b = stack.pop(); a = stack.pop(); stack.push(a <= b); break; case 'ln': a = stack.pop(); stack.push(Math.log(a)); break; case 'log': a = stack.pop(); stack.push(Math.log(a) / Math.LN10); break; case 'lt': b = stack.pop(); a = stack.pop(); stack.push(a < b); break; case 'mod': b = stack.pop(); a = stack.pop(); stack.push(a % b); break; case 'mul': b = stack.pop(); a = stack.pop(); stack.push(a * b); break; case 'ne': b = stack.pop(); a = stack.pop(); stack.push(a != b); break; case 'neg': a = stack.pop(); stack.push(-b); break; case 'not': a = stack.pop(); if (isBool(a) && isBool(b)) stack.push(a && b); else stack.push(a & b); break; case 'or': b = stack.pop(); a = stack.pop(); if (isBool(a) && isBool(b)) stack.push(a || b); else stack.push(a | b); break; case 'pop': stack.pop(); break; case 'roll': b = stack.pop(); a = stack.pop(); stack.roll(a, b); break; case 'round': a = stack.pop(); stack.push(Math.round(a)); break; case 'sin': a = stack.pop(); stack.push(Math.sin(a)); break; case 'sqrt': a = stack.pop(); stack.push(Math.sqrt(a)); break; case 'sub': b = stack.pop(); a = stack.pop(); stack.push(a - b); break; case 'true': stack.push(true); break; case 'truncate': a = stack.pop(); a = a < 0 ? Math.ceil(a) : Math.floor(a); stack.push(a); break; case 'xor': b = stack.pop(); a = stack.pop(); if (isBool(a) && isBool(b)) stack.push(a != b); else stack.push(a ^ b); break; default: error('Unknown operator ' + operator); break; } } return stack.stack; } }; return PostScriptEvaluator; })(); var PostScriptParser = (function PostScriptParserClosure() { function PostScriptParser(lexer) { this.lexer = lexer; this.operators = []; this.token = null; this.prev = null; } PostScriptParser.prototype = { nextToken: function PostScriptParser_nextToken() { this.prev = this.token; this.token = this.lexer.getToken(); }, accept: function PostScriptParser_accept(type) { if (this.token.type == type) { this.nextToken(); return true; } return false; }, expect: function PostScriptParser_expect(type) { if (this.accept(type)) return true; error('Unexpected symbol: found ' + this.token.type + ' expected ' + type + '.'); }, parse: function PostScriptParser_parse() { this.nextToken(); this.expect(PostScriptTokenTypes.LBRACE); this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); return this.operators; }, parseBlock: function PostScriptParser_parseBlock() { while (true) { if (this.accept(PostScriptTokenTypes.NUMBER)) { this.operators.push(this.prev.value); } else if (this.accept(PostScriptTokenTypes.OPERATOR)) { this.operators.push(this.prev.value); } else if (this.accept(PostScriptTokenTypes.LBRACE)) { this.parseCondition(); } else { return; } } }, parseCondition: function PostScriptParser_parseCondition() { // Add two place holders that will be updated later var conditionLocation = this.operators.length; this.operators.push(null, null); this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); if (this.accept(PostScriptTokenTypes.IF)) { // The true block is right after the 'if' so it just falls through on // true else it jumps and skips the true block. this.operators[conditionLocation] = this.operators.length; this.operators[conditionLocation + 1] = 'jz'; } else if (this.accept(PostScriptTokenTypes.LBRACE)) { var jumpLocation = this.operators.length; this.operators.push(null, null); var endOfTrue = this.operators.length; this.parseBlock(); this.expect(PostScriptTokenTypes.RBRACE); this.expect(PostScriptTokenTypes.IFELSE); // The jump is added at the end of the true block to skip the false // block. this.operators[jumpLocation] = this.operators.length; this.operators[jumpLocation + 1] = 'j'; this.operators[conditionLocation] = endOfTrue; this.operators[conditionLocation + 1] = 'jz'; } else { error('PS Function: error parsing conditional.'); } } }; return PostScriptParser; })(); var PostScriptTokenTypes = { LBRACE: 0, RBRACE: 1, NUMBER: 2, OPERATOR: 3, IF: 4, IFELSE: 5 }; var PostScriptToken = (function PostScriptTokenClosure() { function PostScriptToken(type, value) { this.type = type; this.value = value; } var opCache = {}; PostScriptToken.getOperator = function PostScriptToken_getOperator(op) { var opValue = opCache[op]; if (opValue) return opValue; return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op); }; PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE, '{'); PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE, '}'); PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF'); PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE, 'IFELSE'); return PostScriptToken; })(); var PostScriptLexer = (function PostScriptLexerClosure() { function PostScriptLexer(stream) { this.stream = stream; this.nextChar(); } PostScriptLexer.prototype = { nextChar: function PostScriptLexer_nextChar() { return (this.currentChar = this.stream.getByte()); }, getToken: function PostScriptLexer_getToken() { var s = ''; var comment = false; var ch = this.currentChar; // skip comments while (true) { if (ch < 0) { return EOF; } if (comment) { if (ch === 0x0A || ch === 0x0D) { comment = false; } } else if (ch == 0x25) { // '%' comment = true; } else if (!Lexer.isSpace(ch)) { break; } ch = this.nextChar(); } switch (ch | 0) { case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4' case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9' case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.' return new PostScriptToken(PostScriptTokenTypes.NUMBER, this.getNumber()); case 0x7B: // '{' this.nextChar(); return PostScriptToken.LBRACE; case 0x7D: // '}' this.nextChar(); return PostScriptToken.RBRACE; } // operator var str = String.fromCharCode(ch); while ((ch = this.nextChar()) >= 0 && // and 'A'-'Z', 'a'-'z' ((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) { str += String.fromCharCode(ch); } switch (str.toLowerCase()) { case 'if': return PostScriptToken.IF; case 'ifelse': return PostScriptToken.IFELSE; default: return PostScriptToken.getOperator(str); } }, getNumber: function PostScriptLexer_getNumber() { var ch = this.currentChar; var str = String.fromCharCode(ch); while ((ch = this.nextChar()) >= 0) { if ((ch >= 0x30 && ch <= 0x39) || // '0'-'9' ch === 0x2D || ch === 0x2E) { // '-', '.' str += String.fromCharCode(ch); } else { break; } } var value = parseFloat(str); if (isNaN(value)) error('Invalid floating point number: ' + value); return value; } }; return PostScriptLexer; })(); var Annotation = (function AnnotationClosure() { // 12.5.5: Algorithm: Appearance streams function getTransformMatrix(rect, bbox, matrix) { var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); var minX = bounds[0]; var minY = bounds[1]; var maxX = bounds[2]; var maxY = bounds[3]; if (minX === maxX || minY === maxY) { // From real-life file, bbox was [0, 0, 0, 0]. In this case, // just apply the transform for rect return [1, 0, 0, 1, rect[0], rect[1]]; } var xRatio = (rect[2] - rect[0]) / (maxX - minX); var yRatio = (rect[3] - rect[1]) / (maxY - minY); return [ xRatio, 0, 0, yRatio, rect[0] - minX * xRatio, rect[1] - minY * yRatio ]; } function getDefaultAppearance(dict) { var appearanceState = dict.get('AP'); if (!isDict(appearanceState)) { return; } var appearance; var appearances = appearanceState.get('N'); if (isDict(appearances)) { var as = dict.get('AS'); if (as && appearances.has(as.name)) { appearance = appearances.get(as.name); } } else { appearance = appearances; } return appearance; } function Annotation(params) { if (params.data) { this.data = params.data; return; } var dict = params.dict; var data = this.data = {}; data.subtype = dict.get('Subtype').name; var rect = dict.get('Rect'); data.rect = Util.normalizeRect(rect); data.annotationFlags = dict.get('F'); var color = dict.get('C'); if (isArray(color) && color.length === 3) { // TODO(mack): currently only supporting rgb; need support different // colorspaces data.color = color; } else { data.color = [0, 0, 0]; } // Some types of annotations have border style dict which has more // info than the border array if (dict.has('BS')) { var borderStyle = dict.get('BS'); data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1; } else { var borderArray = dict.get('Border') || [0, 0, 1]; data.borderWidth = borderArray[2] || 0; } this.appearance = getDefaultAppearance(dict); data.hasAppearance = !!this.appearance; } Annotation.prototype = { getData: function Annotation_getData() { return this.data; }, hasHtml: function Annotation_hasHtml() { return false; }, getHtmlElement: function Annotation_getHtmlElement(commonObjs) { throw new NotImplementedException( 'getHtmlElement() should be implemented in subclass'); }, // TODO(mack): Remove this, it's not really that helpful. getEmptyContainer: function Annotation_getEmptyContainer(tagName, rect) { assert(!isWorker, 'getEmptyContainer() should be called from main thread'); rect = rect || this.data.rect; var element = document.createElement(tagName); element.style.width = Math.ceil(rect[2] - rect[0]) + 'px'; element.style.height = Math.ceil(rect[3] - rect[1]) + 'px'; return element; }, isViewable: function Annotation_isViewable() { var data = this.data; return !!( data && (!data.annotationFlags || !(data.annotationFlags & 0x22)) && // Hidden or NoView data.rect // rectangle is nessessary ); }, loadResources: function(keys) { var promise = new Promise(); this.appearance.dict.getAsync('Resources').then(function(resources) { if (!resources) { promise.resolve(); return; } var objectLoader = new ObjectLoader(resources.map, keys, resources.xref); objectLoader.load().then(function() { promise.resolve(resources); }); }.bind(this)); return promise; }, getOperatorList: function Annotation_getToOperatorList(evaluator) { var promise = new Promise(); if (!this.appearance) { promise.resolve(new OperatorList()); return promise; } var data = this.data; var appearanceDict = this.appearance.dict; var resourcesPromise = this.loadResources([ 'ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font' // ProcSet // Properties ]); var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1]; var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; var transform = getTransformMatrix(data.rect, bbox, matrix); var border = data.border; resourcesPromise.then(function(resources) { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); evaluator.getOperatorList(this.appearance, resources, opList); opList.addOp(OPS.endAnnotation, []); promise.resolve(opList); }.bind(this)); return promise; } }; Annotation.getConstructor = function Annotation_getConstructor(subtype, fieldType) { if (!subtype) { return; } // TODO(mack): Implement FreeText annotations if (subtype === 'Link') { return LinkAnnotation; } else if (subtype === 'Text') { return TextAnnotation; } else if (subtype === 'Widget') { if (!fieldType) { return; } if (fieldType === 'Tx') { return TextWidgetAnnotation; } else { return WidgetAnnotation; } } else { return Annotation; } }; // TODO(mack): Support loading annotation from data Annotation.fromData = function Annotation_fromData(data) { var subtype = data.subtype; var fieldType = data.fieldType; var Constructor = Annotation.getConstructor(subtype, fieldType); if (Constructor) { return new Constructor({ data: data }); } }; Annotation.fromRef = function Annotation_fromRef(xref, ref) { var dict = xref.fetchIfRef(ref); if (!isDict(dict)) { return; } var subtype = dict.get('Subtype'); subtype = isName(subtype) ? subtype.name : ''; if (!subtype) { return; } var fieldType = Util.getInheritableProperty(dict, 'FT'); fieldType = isName(fieldType) ? fieldType.name : ''; var Constructor = Annotation.getConstructor(subtype, fieldType); if (!Constructor) { return; } var params = { dict: dict, ref: ref, }; var annotation = new Constructor(params); if (annotation.isViewable()) { return annotation; } else { TODO('unimplemented annotation type: ' + subtype); } }; Annotation.appendToOperatorList = function Annotation_appendToOperatorList( annotations, opList, pdfManager, partialEvaluator) { function reject(e) { annotationsReadyPromise.reject(e); } var annotationsReadyPromise = new Promise(); var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { annotationPromises.push(annotations[i].getOperatorList(partialEvaluator)); } Promise.all(annotationPromises).then(function(datas) { opList.addOp(OPS.beginAnnotations, []); for (var i = 0, n = datas.length; i < n; ++i) { var annotOpList = datas[i]; opList.addOpList(annotOpList); } opList.addOp(OPS.endAnnotations, []); annotationsReadyPromise.resolve(); }, reject); return annotationsReadyPromise; }; return Annotation; })(); PDFJS.Annotation = Annotation; var WidgetAnnotation = (function WidgetAnnotationClosure() { function WidgetAnnotation(params) { Annotation.call(this, params); if (params.data) { return; } var dict = params.dict; var data = this.data; data.fieldValue = stringToPDFString( Util.getInheritableProperty(dict, 'V') || ''); data.alternativeText = stringToPDFString(dict.get('TU') || ''); data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || ''; var fieldType = Util.getInheritableProperty(dict, 'FT'); data.fieldType = isName(fieldType) ? fieldType.name : ''; data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict(); // Building the full field name by collecting the field and // its ancestors 'T' data and joining them using '.'. var fieldName = []; var namedItem = dict; var ref = params.ref; 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; } data.fullName = fieldName.join('.'); } var parent = Annotation.prototype; Util.inherit(WidgetAnnotation, Annotation, { isViewable: function WidgetAnnotation_isViewable() { if (this.data.fieldType === 'Sig') { TODO('unimplemented annotation type: Widget signature'); return false; } return parent.isViewable.call(this); } }); return WidgetAnnotation; })(); var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { function TextWidgetAnnotation(params) { WidgetAnnotation.call(this, params); if (params.data) { return; } this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q'); } // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont() function setTextStyles(element, item, fontObj) { var style = element.style; style.fontSize = item.fontSize + 'px'; style.direction = item.fontDirection < 0 ? 'rtl': 'ltr'; if (!fontObj) { return; } style.fontWeight = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : (fontObj.bold ? 'bold' : 'normal'); style.fontStyle = fontObj.italic ? 'italic' : 'normal'; var fontName = fontObj.loadedName; var fontFamily = fontName ? '"' + fontName + '", ' : ''; // Use a reasonable default font if the font doesn't specify a fallback var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif'; style.fontFamily = fontFamily + fallbackName; } var parent = WidgetAnnotation.prototype; Util.inherit(TextWidgetAnnotation, WidgetAnnotation, { hasHtml: function TextWidgetAnnotation_hasHtml() { return !this.data.hasAppearance && !!this.data.fieldValue; }, getHtmlElement: function TextWidgetAnnotation_getHtmlElement(commonObjs) { assert(!isWorker, 'getHtmlElement() shall be called from main thread'); var item = this.data; var element = this.getEmptyContainer('div'); element.style.display = 'table'; var content = document.createElement('div'); content.textContent = item.fieldValue; var textAlignment = item.textAlignment; content.style.textAlign = ['left', 'center', 'right'][textAlignment]; content.style.verticalAlign = 'middle'; content.style.display = 'table-cell'; var fontObj = item.fontRefName ? commonObjs.getData(item.fontRefName) : null; var cssRules = setTextStyles(content, item, fontObj); element.appendChild(content); return element; }, getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { if (this.appearance) { return Annotation.prototype.getOperatorList.call(this, evaluator); } var promise = new Promise(); var opList = new OperatorList(); var data = this.data; // Even if there is an appearance stream, ignore it. This is the // behaviour used by Adobe Reader. var defaultAppearance = data.defaultAppearance; if (!defaultAppearance) { promise.resolve(opList); return promise; } // Include any font resources found in the default appearance var stream = new Stream(stringToBytes(defaultAppearance)); evaluator.getOperatorList(stream, this.fieldResources, opList); var appearanceFnArray = opList.fnArray; var appearanceArgsArray = opList.argsArray; var fnArray = []; var argsArray = []; // TODO(mack): Add support for stroke color data.rgb = [0, 0, 0]; // TODO THIS DOESN'T MAKE ANY SENSE SINCE THE fnArray IS EMPTY! for (var i = 0, n = fnArray.length; i < n; ++i) { var fnId = appearanceFnArray[i]; var args = appearanceArgsArray[i]; if (fnId === OPS.setFont) { data.fontRefName = args[0]; var size = args[1]; if (size < 0) { data.fontDirection = -1; data.fontSize = -size; } else { data.fontDirection = 1; data.fontSize = size; } } else if (fnId === OPS.setFillRGBColor) { data.rgb = args; } else if (fnId === OPS.setFillGray) { var rgbValue = args[0] * 255; data.rgb = [rgbValue, rgbValue, rgbValue]; } } promise.resolve(opList); return promise; } }); return TextWidgetAnnotation; })(); var TextAnnotation = (function TextAnnotationClosure() { function TextAnnotation(params) { Annotation.call(this, params); if (params.data) { return; } var dict = params.dict; var data = this.data; var content = dict.get('Contents'); var title = dict.get('T'); data.content = stringToPDFString(content || ''); data.title = stringToPDFString(title || ''); data.name = !dict.has('Name') ? 'Note' : dict.get('Name').name; } var ANNOT_MIN_SIZE = 10; Util.inherit(TextAnnotation, Annotation, { getOperatorList: function TextAnnotation_getOperatorList(evaluator) { var promise = new Promise(); promise.resolve(new OperatorList()); return promise; }, hasHtml: function TextAnnotation_hasHtml() { return true; }, getHtmlElement: function TextAnnotation_getHtmlElement(commonObjs) { assert(!isWorker, 'getHtmlElement() shall be called from main thread'); var item = this.data; var rect = item.rect; // sanity check because of OOo-generated PDFs if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) { rect[3] = rect[1] + ANNOT_MIN_SIZE; } if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) { rect[2] = rect[0] + (rect[3] - rect[1]); // make it square } var container = this.getEmptyContainer('section', rect); container.className = 'annotText'; var image = document.createElement('img'); image.style.height = container.style.height; var iconName = item.name; image.src = PDFJS.imageResourcesPath + 'annotation-' + iconName.toLowerCase() + '.svg'; image.alt = '[{{type}} Annotation]'; image.dataset.l10nId = 'text_annotation_type'; image.dataset.l10nArgs = JSON.stringify({type: iconName}); var content = document.createElement('div'); content.setAttribute('hidden', true); var title = document.createElement('h1'); var text = document.createElement('p'); content.style.left = Math.floor(rect[2] - rect[0]) + 'px'; content.style.top = '0px'; title.textContent = item.title; if (!item.content && !item.title) { content.setAttribute('hidden', true); } else { var e = document.createElement('span'); var lines = item.content.split(/(?:\r\n?|\n)/); for (var i = 0, ii = lines.length; i < ii; ++i) { var line = lines[i]; e.appendChild(document.createTextNode(line)); if (i < (ii - 1)) e.appendChild(document.createElement('br')); } text.appendChild(e); var showAnnotation = function showAnnotation() { container.style.zIndex += 1; content.removeAttribute('hidden'); }; var hideAnnotation = function hideAnnotation(e) { if (e.toElement || e.relatedTarget) { // No context menu is used container.style.zIndex -= 1; content.setAttribute('hidden', true); } }; content.addEventListener('mouseover', showAnnotation, false); content.addEventListener('mouseout', hideAnnotation, false); image.addEventListener('mouseover', showAnnotation, false); image.addEventListener('mouseout', hideAnnotation, false); } content.appendChild(title); content.appendChild(text); container.appendChild(image); container.appendChild(content); return container; } }); return TextAnnotation; })(); var LinkAnnotation = (function LinkAnnotationClosure() { function LinkAnnotation(params) { Annotation.call(this, params); if (params.data) { return; } var dict = params.dict; var data = this.data; var action = dict.get('A'); if (action) { var linkType = action.get('S').name; if (linkType === 'URI') { var url = addDefaultProtocolToUrl(action.get('URI')); // TODO: pdf spec mentions urls can be relative to a Base // entry in the dictionary. if (!isValidUrl(url, false)) { url = ''; } data.url = url; } else if (linkType === 'GoTo') { data.dest = action.get('D'); } else if (linkType === 'GoToR') { var urlDict = action.get('F'); if (isDict(urlDict)) { // We assume that the 'url' is a Filspec dictionary // and fetch the url without checking any further url = urlDict.get('F') || ''; } // TODO: pdf reference says that GoToR // can also have 'NewWindow' attribute if (!isValidUrl(url, false)) { url = ''; } data.url = url; data.dest = action.get('D'); } else if (linkType === 'Named') { data.action = action.get('N').name; } else { TODO('unrecognized link type: ' + linkType); } } else if (dict.has('Dest')) { // simple destination link var dest = dict.get('Dest'); data.dest = isName(dest) ? dest.name : dest; } } // Lets URLs beginning with 'www.' default to using the 'http://' protocol. function addDefaultProtocolToUrl(url) { if (url.indexOf('www.') === 0) { return ('http://' + url); } return url; } Util.inherit(LinkAnnotation, Annotation, { hasOperatorList: function LinkAnnotation_hasOperatorList() { return false; }, hasHtml: function LinkAnnotation_hasHtml() { return true; }, getHtmlElement: function LinkAnnotation_getHtmlElement(commonObjs) { var rect = this.data.rect; var element = document.createElement('a'); var borderWidth = this.data.borderWidth; element.style.borderWidth = borderWidth + 'px'; var color = this.data.color; var rgb = []; for (var i = 0; i < 3; ++i) { rgb[i] = Math.round(color[i] * 255); } element.style.borderColor = Util.makeCssRgb(rgb); element.style.borderStyle = 'solid'; var width = rect[2] - rect[0] - 2 * borderWidth; var height = rect[3] - rect[1] - 2 * borderWidth; element.style.width = width + 'px'; element.style.height = height + 'px'; element.href = this.data.url || ''; return element; } }); return LinkAnnotation; })(); /** * The maximum allowed image size in total pixels e.g. width * height. Images * above this value will not be drawn. Use -1 for no limit. * @var {Number} */ PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize; /** * By default fonts are converted to OpenType fonts and loaded via font face * rules. If disabled, the font will be rendered using a built in font renderer * that constructs the glyphs with primitive path commands. * @var {Boolean} */ PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? false : PDFJS.disableFontFace; /** * Path for image resources, mainly for annotation icons. Include trailing * slash. * @var {String} */ PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ? '' : PDFJS.imageResourcesPath; /** * Disable the web worker and run all code on the main thread. This will happen * automatically if the browser doesn't support workers or sending typed arrays * to workers. * @var {Boolean} */ PDFJS.disableWorker = PDFJS.disableWorker === undefined ? false : PDFJS.disableWorker; /** * Path and filename of the worker file. Required when the worker is enabled in * development mode. If unspecified in the production build, the worker will be * loaded based on the location of the pdf.js file. * @var {String} */ PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc; /** * Disable range request loading of PDF files. When enabled and if the server * supports partial content requests then the PDF will be fetched in chunks. * Enabled (false) by default. * @var {Boolean} */ PDFJS.disableRange = PDFJS.disableRange === undefined ? false : PDFJS.disableRange; /** * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js * will automatically keep fetching more data even if it isn't needed to display * the current page. This default behavior can be disabled. * @var {Boolean} */ PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ? false : PDFJS.disableAutoFetch; /** * Enables special hooks for debugging PDF.js. * @var {Boolean} */ PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug; /** * Enables transfer usage in postMessage for ArrayBuffers. * @var {boolean} */ PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ? true : PDFJS.postMessageTransfers; /** * This is the main entry point for loading a PDF and interacting with it. * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR) * is used, which means it must follow the same origin rules that any XHR does * e.g. No cross domain requests without CORS. * * @param {string|TypedAray|object} source Can be an url to where a PDF is * located, a typed array (Uint8Array) already populated with data or * and parameter object with the following possible fields: * - url - The URL of the PDF. * - data - A typed array with PDF data. * - httpHeaders - Basic authentication headers. * - password - For decrypting password-protected PDFs. * - initialData - A typed array with the first portion or all of the pdf data. * Used by the extension since some data is already loaded * before the switch to range requests. * * @param {object} pdfDataRangeTransport is optional. It is used if you want * to manually serve range requests for data in the PDF. See viewer.js for * an example of pdfDataRangeTransport's interface. * * @param {function} passwordCallback is optional. It is used to request a * password if wrong or no password was provided. The callback receives two * parameters: function that needs to be called with new password and reason * (see {PasswordResponses}). * * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object. */ PDFJS.getDocument = function getDocument(source, pdfDataRangeTransport, passwordCallback, progressCallback) { var workerInitializedPromise, workerReadyPromise, transport; if (typeof source === 'string') { source = { url: source }; } else if (isArrayBuffer(source)) { source = { data: source }; } else if (typeof source !== 'object') { error('Invalid parameter in getDocument, need either Uint8Array, ' + 'string or a parameter object'); } if (!source.url && !source.data) error('Invalid parameter array, need either .data or .url'); // copy/use all keys as is except 'url' -- full path is required var params = {}; for (var key in source) { if (key === 'url' && typeof window !== 'undefined') { params[key] = combineUrl(window.location.href, source[key]); continue; } params[key] = source[key]; } workerInitializedPromise = new PDFJS.Promise(); workerReadyPromise = new PDFJS.Promise(); transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise, pdfDataRangeTransport, progressCallback); workerInitializedPromise.then(function transportInitialized() { transport.passwordCallback = passwordCallback; transport.fetchDocument(params); }); return workerReadyPromise; }; /** * Proxy to a PDFDocument in the worker thread. Also, contains commonly used * properties that can be read synchronously. */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { function PDFDocumentProxy(pdfInfo, transport) { this.pdfInfo = pdfInfo; this.transport = transport; } PDFDocumentProxy.prototype = { /** * @return {number} Total number of pages the PDF contains. */ get numPages() { return this.pdfInfo.numPages; }, /** * @return {string} A unique ID to identify a PDF. Not guaranteed to be * unique. */ get fingerprint() { return this.pdfInfo.fingerprint; }, /** * @return {boolean} true if embedded document fonts are in use. Will be * set during rendering of the pages. */ get embeddedFontsUsed() { return this.transport.embeddedFontsUsed; }, /** * @param {number} The page number to get. The first page is 1. * @return {Promise} A promise that is resolved with a {PDFPageProxy} * object. */ getPage: function PDFDocumentProxy_getPage(number) { return this.transport.getPage(number); }, /** * @param {object} Must have 'num' and 'gen' properties. * @return {Promise} A promise that is resolved with the page index that is * associated with the reference. */ getPageIndex: function PDFDocumentProxy_getPageIndex(ref) { return this.transport.getPageIndex(ref); }, /** * @return {Promise} A promise that is resolved with a lookup table for * mapping named destinations to reference numbers. */ getDestinations: function PDFDocumentProxy_getDestinations() { return this.transport.getDestinations(); }, /** * @return {Promise} A promise that is resolved with an array of all the * JavaScript strings in the name tree. */ getJavaScript: function PDFDocumentProxy_getDestinations() { var promise = new PDFJS.Promise(); var js = this.pdfInfo.javaScript; promise.resolve(js); return promise; }, /** * @return {Promise} A promise that is resolved with an {array} that is a * tree outline (if it has one) of the PDF. The tree is in the format of: * [ * { * title: string, * bold: boolean, * italic: boolean, * color: rgb array, * dest: dest obj, * items: array of more items like this * }, * ... * ]. */ getOutline: function PDFDocumentProxy_getOutline() { var promise = new PDFJS.Promise(); var outline = this.pdfInfo.outline; promise.resolve(outline); return promise; }, /** * @return {Promise} A promise that is resolved with an {object} that has * info and metadata properties. Info is an {object} filled with anything * available in the information dictionary and similarly metadata is a * {Metadata} object with information from the metadata section of the PDF. */ getMetadata: function PDFDocumentProxy_getMetadata() { var promise = new PDFJS.Promise(); var info = this.pdfInfo.info; var metadata = this.pdfInfo.metadata; promise.resolve({ info: info, metadata: metadata ? new PDFJS.Metadata(metadata) : null }); return promise; }, isEncrypted: function PDFDocumentProxy_isEncrypted() { var promise = new PDFJS.Promise(); promise.resolve(this.pdfInfo.encrypted); return promise; }, /** * @return {Promise} A promise that is resolved with a TypedArray that has * the raw data from the PDF. */ getData: function PDFDocumentProxy_getData() { var promise = new PDFJS.Promise(); this.transport.getData(promise); return promise; }, /** * @return {Promise} A promise that is resolved when the document's data * is loaded */ dataLoaded: function PDFDocumentProxy_dataLoaded() { return this.transport.dataLoaded(); }, cleanup: function PDFDocumentProxy_cleanup() { this.transport.startCleanup(); }, destroy: function PDFDocumentProxy_destroy() { this.transport.destroy(); } }; return PDFDocumentProxy; })(); var PDFPageProxy = (function PDFPageProxyClosure() { function PDFPageProxy(pageInfo, transport) { this.pageInfo = pageInfo; this.transport = transport; this.stats = new StatTimer(); this.stats.enabled = !!globalScope.PDFJS.enableStats; this.commonObjs = transport.commonObjs; this.objs = new PDFObjects(); this.receivingOperatorList = false; this.cleanupAfterRender = false; this.pendingDestroy = false; this.renderTasks = []; } PDFPageProxy.prototype = { /** * @return {number} Page number of the page. First page is 1. */ get pageNumber() { return this.pageInfo.pageIndex + 1; }, /** * @return {number} The number of degrees the page is rotated clockwise. */ get rotate() { return this.pageInfo.rotate; }, /** * @return {object} The reference that points to this page. It has 'num' and * 'gen' properties. */ get ref() { return this.pageInfo.ref; }, /** * @return {array} An array of the visible portion of the PDF page in the * user space units - [x1, y1, x2, y2]. */ get view() { return this.pageInfo.view; }, /** * @param {number} scale The desired scale of the viewport. * @param {number} rotate Degrees to rotate the viewport. If omitted this * defaults to the page rotation. * @return {PageViewport} Contains 'width' and 'height' properties along * with transforms required for rendering. */ getViewport: function PDFPageProxy_getViewport(scale, rotate) { if (arguments.length < 2) rotate = this.rotate; return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); }, /** * @return {Promise} A promise that is resolved with an {array} of the * annotation objects. */ getAnnotations: function PDFPageProxy_getAnnotations() { if (this.annotationsPromise) return this.annotationsPromise; var promise = new PDFJS.Promise(); this.annotationsPromise = promise; this.transport.getAnnotations(this.pageInfo.pageIndex); return promise; }, /** * Begins the process of rendering a page to the desired context. * @param {object} params A parameter object that supports: * { * canvasContext(required): A 2D context of a DOM Canvas object., * textLayer(optional): An object that has beginLayout, endLayout, and * appendText functions., * imageLayer(optional): An object that has beginLayout, endLayout and * appendImage functions., * continueCallback(optional): A function that will be called each time * the rendering is paused. To continue * rendering call the function that is the * first argument to the callback. * }. * @return {RenderTask} An extended promise that is resolved when the page * finishes rendering (see RenderTask). */ render: function PDFPageProxy_render(params) { var stats = this.stats; stats.time('Overall'); // If there was a pending destroy cancel it so no cleanup happens during // this call to render. this.pendingDestroy = false; // If there is no displayReadyPromise yet, then the operatorList was never // requested before. Make the request and create the promise. if (!this.displayReadyPromise) { this.receivingOperatorList = true; this.displayReadyPromise = new Promise(); this.operatorList = { fnArray: [], argsArray: [], lastChunk: false }; this.stats.time('Page Request'); this.transport.messageHandler.send('RenderPageRequest', { pageIndex: this.pageNumber - 1 }); } var internalRenderTask = new InternalRenderTask(complete, params, this.objs, this.commonObjs, this.operatorList, this.pageNumber); this.renderTasks.push(internalRenderTask); var renderTask = new RenderTask(internalRenderTask); var self = this; this.displayReadyPromise.then( function pageDisplayReadyPromise(transparency) { if (self.pendingDestroy) { complete(); return; } stats.time('Rendering'); internalRenderTask.initalizeGraphics(transparency); internalRenderTask.operatorListChanged(); }, function pageDisplayReadPromiseError(reason) { complete(reason); } ); function complete(error) { var i = self.renderTasks.indexOf(internalRenderTask); if (i >= 0) { self.renderTasks.splice(i, 1); } if (self.cleanupAfterRender) { self.pendingDestroy = true; } self._tryDestroy(); if (error) { renderTask.reject(error); } else { renderTask.resolve(); } stats.timeEnd('Rendering'); stats.timeEnd('Overall'); } return renderTask; }, /** * @return {Promise} That is resolved with the a {string} that is the text * content from the page. */ getTextContent: function PDFPageProxy_getTextContent() { var promise = new PDFJS.Promise(); this.transport.messageHandler.send('GetTextContent', { pageIndex: this.pageNumber - 1 }, function textContentCallback(textContent) { promise.resolve(textContent); } ); return promise; }, /** * Stub for future feature. */ getOperationList: function PDFPageProxy_getOperationList() { var promise = new PDFJS.Promise(); var operationList = { // not implemented dependencyFontsID: null, operatorList: null }; promise.resolve(operationList); return promise; }, /** * Destroys resources allocated by the page. */ destroy: function PDFPageProxy_destroy() { this.pendingDestroy = true; this._tryDestroy(); }, /** * For internal use only. Attempts to clean up if rendering is in a state * where that's possible. */ _tryDestroy: function PDFPageProxy__destroy() { if (!this.pendingDestroy || this.renderTasks.length !== 0 || this.receivingOperatorList) { return; } delete this.operatorList; delete this.displayReadyPromise; this.objs.clear(); this.pendingDestroy = false; }, /** * For internal use only. */ _startRenderPage: function PDFPageProxy_startRenderPage(transparency) { this.displayReadyPromise.resolve(transparency); }, /** * For internal use only. */ _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) { // Add the new chunk to the current operator list. for (var i = 0, ii = operatorListChunk.length; i < ii; i++) { this.operatorList.fnArray.push(operatorListChunk.fnArray[i]); this.operatorList.argsArray.push(operatorListChunk.argsArray[i]); } this.operatorList.lastChunk = operatorListChunk.lastChunk; // Notify all the rendering tasks there are more operators to be consumed. for (var i = 0; i < this.renderTasks.length; i++) { this.renderTasks[i].operatorListChanged(); } if (operatorListChunk.lastChunk) { this.receivingOperatorList = false; this._tryDestroy(); } } }; return PDFPageProxy; })(); /** * For internal use only. */ var WorkerTransport = (function WorkerTransportClosure() { function WorkerTransport(workerInitializedPromise, workerReadyPromise, pdfDataRangeTransport, progressCallback) { this.pdfDataRangeTransport = pdfDataRangeTransport; this.workerReadyPromise = workerReadyPromise; this.progressCallback = progressCallback; this.commonObjs = new PDFObjects(); this.pageCache = []; this.pagePromises = []; this.embeddedFontsUsed = false; this.passwordCallback = null; // If worker support isn't disabled explicit and the browser has worker // support, create a new web worker and test if it/the browser fullfills // all requirements to run parts of pdf.js in a web worker. // Right now, the requirement is, that an Uint8Array is still an Uint8Array // as it arrives on the worker. Chrome added this with version 15. if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { var workerSrc = PDFJS.workerSrc; if (!workerSrc) { error('No PDFJS.workerSrc specified'); } try { // Some versions of FF can't create a worker on localhost, see: // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 var worker = new Worker(workerSrc); var messageHandler = new MessageHandler('main', worker); this.messageHandler = messageHandler; messageHandler.on('test', function transportTest(data) { var supportTypedArray = data && data.supportTypedArray; if (supportTypedArray) { this.worker = worker; if (!data.supportTransfers) { PDFJS.postMessageTransfers = false; } this.setupMessageHandler(messageHandler); workerInitializedPromise.resolve(); } else { globalScope.PDFJS.disableWorker = true; this.loadFakeWorkerFiles().then(function() { this.setupFakeWorker(); workerInitializedPromise.resolve(); }.bind(this)); } }.bind(this)); var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); // Some versions of Opera throw a DATA_CLONE_ERR on serializing the // typed array. Also, checking if we can use transfers. try { messageHandler.send('test', testObj, null, [testObj.buffer]); } catch (ex) { info('Cannot use postMessage transfers'); testObj[0] = 0; messageHandler.send('test', testObj); } return; } catch (e) { info('The worker has been disabled.'); } } // Either workers are disabled, not supported or have thrown an exception. // Thus, we fallback to a faked worker. globalScope.PDFJS.disableWorker = true; this.loadFakeWorkerFiles().then(function() { this.setupFakeWorker(); workerInitializedPromise.resolve(); }.bind(this)); } WorkerTransport.prototype = { destroy: function WorkerTransport_destroy() { this.pageCache = []; this.pagePromises = []; var self = this; this.messageHandler.send('Terminate', null, function () { if (self.worker) { self.worker.terminate(); } }); }, loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() { if (!PDFJS.fakeWorkerFilesLoadedPromise) { PDFJS.fakeWorkerFilesLoadedPromise = new Promise(); // In the developer build load worker_loader which in turn loads all the // other files and resolves the promise. In production only the // pdf.worker.js file is needed. Util.loadScript(PDFJS.workerSrc, function() { PDFJS.fakeWorkerFilesLoadedPromise.resolve(); }); } return PDFJS.fakeWorkerFilesLoadedPromise; }, setupFakeWorker: function WorkerTransport_setupFakeWorker() { warn('Setting up fake worker.'); // If we don't use a worker, just post/sendMessage to the main thread. var fakeWorker = { postMessage: function WorkerTransport_postMessage(obj) { fakeWorker.onmessage({data: obj}); }, terminate: function WorkerTransport_terminate() {} }; var messageHandler = new MessageHandler('main', fakeWorker); this.setupMessageHandler(messageHandler); // If the main thread is our worker, setup the handling for the messages // the main thread sends to it self. PDFJS.WorkerMessageHandler.setup(messageHandler); }, setupMessageHandler: function WorkerTransport_setupMessageHandler(messageHandler) { this.messageHandler = messageHandler; function updatePassword(password) { messageHandler.send('UpdatePassword', password); } var pdfDataRangeTransport = this.pdfDataRangeTransport; if (pdfDataRangeTransport) { pdfDataRangeTransport.addRangeListener(function(begin, chunk) { messageHandler.send('OnDataRange', { begin: begin, chunk: chunk }); }); pdfDataRangeTransport.addProgressListener(function(loaded) { messageHandler.send('OnDataProgress', { loaded: loaded }); }); messageHandler.on('RequestDataRange', function transportDataRange(data) { pdfDataRangeTransport.requestDataRange(data.begin, data.end); }, this); } messageHandler.on('GetDoc', function transportDoc(data) { var pdfInfo = data.pdfInfo; var pdfDocument = new PDFDocumentProxy(pdfInfo, this); this.pdfDocument = pdfDocument; this.workerReadyPromise.resolve(pdfDocument); }, this); messageHandler.on('NeedPassword', function transportPassword(data) { if (this.passwordCallback) { return this.passwordCallback(updatePassword, PasswordResponses.NEED_PASSWORD); } this.workerReadyPromise.reject(data.exception.message, data.exception); }, this); messageHandler.on('IncorrectPassword', function transportBadPass(data) { if (this.passwordCallback) { return this.passwordCallback(updatePassword, PasswordResponses.INCORRECT_PASSWORD); } this.workerReadyPromise.reject(data.exception.message, data.exception); }, this); messageHandler.on('InvalidPDF', function transportInvalidPDF(data) { this.workerReadyPromise.reject(data.exception.name, data.exception); }, this); messageHandler.on('MissingPDF', function transportMissingPDF(data) { this.workerReadyPromise.reject(data.exception.message, data.exception); }, this); messageHandler.on('UnknownError', function transportUnknownError(data) { this.workerReadyPromise.reject(data.exception.message, data.exception); }, this); messageHandler.on('GetPage', function transportPage(data) { var pageInfo = data.pageInfo; var page = new PDFPageProxy(pageInfo, this); this.pageCache[pageInfo.pageIndex] = page; var promise = this.pagePromises[pageInfo.pageIndex]; promise.resolve(page); }, this); messageHandler.on('GetAnnotations', function transportAnnotations(data) { var annotations = data.annotations; var promise = this.pageCache[data.pageIndex].annotationsPromise; promise.resolve(annotations); }, this); messageHandler.on('StartRenderPage', function transportRender(data) { var page = this.pageCache[data.pageIndex]; page.stats.timeEnd('Page Request'); page._startRenderPage(data.transparency); }, this); messageHandler.on('RenderPageChunk', function transportRender(data) { var page = this.pageCache[data.pageIndex]; page._renderPageChunk(data.operatorList); }, this); messageHandler.on('commonobj', function transportObj(data) { var id = data[0]; var type = data[1]; if (this.commonObjs.hasData(id)) return; switch (type) { case 'Font': var exportedData = data[2]; var font; if ('error' in exportedData) { var error = exportedData.error; warn('Error during font loading: ' + error); this.commonObjs.resolve(id, error); break; } else { font = new FontFace(exportedData); } FontLoader.bind( [font], function fontReady(fontObjs) { this.commonObjs.resolve(id, font); }.bind(this) ); break; case 'FontPath': this.commonObjs.resolve(id, data[2]); break; default: error('Got unknown common object type ' + type); } }, this); messageHandler.on('obj', function transportObj(data) { var id = data[0]; var pageIndex = data[1]; var type = data[2]; var pageProxy = this.pageCache[pageIndex]; if (pageProxy.objs.hasData(id)) return; switch (type) { case 'JpegStream': var imageData = data[3]; loadJpegStream(id, imageData, pageProxy.objs); break; case 'Image': var imageData = data[3]; pageProxy.objs.resolve(id, imageData); // heuristics that will allow not to store large data var MAX_IMAGE_SIZE_TO_STORE = 8000000; if ('data' in imageData && imageData.data.length > MAX_IMAGE_SIZE_TO_STORE) { pageProxy.cleanupAfterRender = true; } break; default: error('Got unknown object type ' + type); } }, this); messageHandler.on('DocProgress', function transportDocProgress(data) { if (this.progressCallback) { this.progressCallback({ loaded: data.loaded, total: data.total }); } }, this); messageHandler.on('DocError', function transportDocError(data) { this.workerReadyPromise.reject(data); }, this); messageHandler.on('PageError', function transportError(data) { var page = this.pageCache[data.pageNum - 1]; if (page.displayReadyPromise) page.displayReadyPromise.reject(data.error); else error(data.error); }, this); messageHandler.on('JpegDecode', function(data, promise) { var imageUrl = 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); img.src = imageUrl; }); }, fetchDocument: function WorkerTransport_fetchDocument(source) { source.disableAutoFetch = PDFJS.disableAutoFetch; source.chunkedViewerLoading = !!this.pdfDataRangeTransport; this.messageHandler.send('GetDocRequest', { source: source, disableRange: PDFJS.disableRange, maxImageSize: PDFJS.maxImageSize, disableFontFace: PDFJS.disableFontFace }); }, getData: function WorkerTransport_getData(promise) { this.messageHandler.send('GetData', null, function(data) { promise.resolve(data); }); }, dataLoaded: function WorkerTransport_dataLoaded() { var promise = new PDFJS.Promise(); this.messageHandler.send('DataLoaded', null, function(args) { promise.resolve(args); }); return promise; }, getPage: function WorkerTransport_getPage(pageNumber, promise) { var pageIndex = pageNumber - 1; if (pageIndex in this.pagePromises) return this.pagePromises[pageIndex]; var promise = new PDFJS.Promise('Page ' + pageNumber); this.pagePromises[pageIndex] = promise; this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); return promise; }, getPageIndex: function WorkerTransport_getPageIndexByRef(ref) { var promise = new PDFJS.Promise(); this.messageHandler.send('GetPageIndex', { ref: ref }, function (pageIndex) { promise.resolve(pageIndex); } ); return promise; }, getAnnotations: function WorkerTransport_getAnnotations(pageIndex) { this.messageHandler.send('GetAnnotationsRequest', { pageIndex: pageIndex }); }, getDestinations: function WorkerTransport_getDestinations() { var promise = new PDFJS.Promise(); this.messageHandler.send('GetDestinations', null, function transportDestinations(destinations) { promise.resolve(destinations); } ); return promise; }, startCleanup: function WorkerTransport_startCleanup() { this.messageHandler.send('Cleanup', null, function endCleanup() { for (var i = 0, ii = this.pageCache.length; i < ii; i++) { var page = this.pageCache[i]; if (page) { page.destroy(); } } this.commonObjs.clear(); FontLoader.clear(); }.bind(this) ); } }; return WorkerTransport; })(); /** * A PDF document and page is built of many objects. E.g. there are objects * for fonts, images, rendering code and such. These objects might get processed * inside of a worker. The `PDFObjects` implements some basic functions to * manage these objects. */ var PDFObjects = (function PDFObjectsClosure() { function PDFObjects() { this.objs = {}; } PDFObjects.prototype = { /** * Internal function. * Ensures there is an object defined for `objId`. */ ensureObj: function PDFObjects_ensureObj(objId) { if (this.objs[objId]) return this.objs[objId]; var obj = { promise: new Promise(objId), data: null, resolved: false }; this.objs[objId] = obj; return obj; }, /** * If called *without* callback, this returns the data of `objId` but the * object needs to be resolved. If it isn't, this function throws. * * If called *with* a callback, the callback is called with the data of the * object once the object is resolved. That means, if you call this * function and the object is already resolved, the callback gets called * right away. */ get: function PDFObjects_get(objId, callback) { // If there is a callback, then the get can be async and the object is // not required to be resolved right now if (callback) { this.ensureObj(objId).promise.then(callback); return null; } // If there isn't a callback, the user expects to get the resolved data // directly. var obj = this.objs[objId]; // If there isn't an object yet or the object isn't resolved, then the // data isn't ready yet! if (!obj || !obj.resolved) error('Requesting object that isn\'t resolved yet ' + objId); return obj.data; }, /** * Resolves the object `objId` with optional `data`. */ resolve: function PDFObjects_resolve(objId, data) { var obj = this.ensureObj(objId); obj.resolved = true; obj.data = data; obj.promise.resolve(data); }, isResolved: function PDFObjects_isResolved(objId) { var objs = this.objs; if (!objs[objId]) { return false; } else { return objs[objId].resolved; } }, hasData: function PDFObjects_hasData(objId) { return this.isResolved(objId); }, /** * Returns the data of `objId` if object exists, null otherwise. */ getData: function PDFObjects_getData(objId) { var objs = this.objs; if (!objs[objId] || !objs[objId].resolved) { return null; } else { return objs[objId].data; } }, clear: function PDFObjects_clear() { this.objs = {}; } }; return PDFObjects; })(); /* * RenderTask is basically a promise but adds a cancel function to terminate it. */ var RenderTask = (function RenderTaskClosure() { function RenderTask(internalRenderTask) { this.internalRenderTask = internalRenderTask; Promise.call(this); } RenderTask.prototype = Object.create(Promise.prototype); /** * Cancel the rendering task. If the task is curently rendering it will not be * cancelled until graphics pauses with a timeout. The promise that this * object extends will resolved when cancelled. */ RenderTask.prototype.cancel = function RenderTask_cancel() { this.internalRenderTask.cancel(); }; return RenderTask; })(); var InternalRenderTask = (function InternalRenderTaskClosure() { function InternalRenderTask(callback, params, objs, commonObjs, operatorList, pageNumber) { this.callback = callback; this.params = params; this.objs = objs; this.commonObjs = commonObjs; this.operatorListIdx = null; this.operatorList = operatorList; this.pageNumber = pageNumber; this.running = false; this.graphicsReadyCallback = null; this.graphicsReady = false; this.cancelled = false; } InternalRenderTask.prototype = { initalizeGraphics: function InternalRenderTask_initalizeGraphics(transparency) { if (this.cancelled) { return; } if (PDFJS.pdfBug && 'StepperManager' in globalScope && globalScope.StepperManager.enabled) { this.stepper = globalScope.StepperManager.create(this.pageNumber - 1); this.stepper.init(this.operatorList); this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); } var params = this.params; this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, this.objs, params.textLayer, params.imageLayer); this.gfx.beginDrawing(params.viewport, transparency); this.operatorListIdx = 0; this.graphicsReady = true; if (this.graphicsReadyCallback) { this.graphicsReadyCallback(); } }, cancel: function InternalRenderTask_cancel() { this.running = false; this.cancelled = true; this.callback('cancelled'); }, operatorListChanged: function InternalRenderTask_operatorListChanged() { if (!this.graphicsReady) { if (!this.graphicsReadyCallback) { this.graphicsReadyCallback = this._continue.bind(this); } return; } if (this.stepper) { this.stepper.updateOperatorList(this.operatorList); } if (this.running) { return; } this._continue(); }, _continue: function InternalRenderTask__continue() { this.running = true; if (this.cancelled) { return; } if (this.params.continueCallback) { this.params.continueCallback(this._next.bind(this)); } else { this._next(); } }, _next: function InternalRenderTask__next() { if (this.cancelled) { return; } this.operatorListIdx = this.gfx.executeOperatorList(this.operatorList, this.operatorListIdx, this._continue.bind(this), this.stepper); if (this.operatorListIdx === this.operatorList.argsArray.length) { this.running = false; if (this.operatorList.lastChunk) { this.gfx.endDrawing(); this.callback(); } } } }; return InternalRenderTask; })(); var Metadata = PDFJS.Metadata = (function MetadataClosure() { function fixMetadata(meta) { return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) { var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g, function(code, d1, d2, d3) { return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1); }); var chars = ''; for (var i = 0; i < bytes.length; i += 2) { var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1); chars += code >= 32 && code < 127 && code != 60 && code != 62 && code != 38 && false ? String.fromCharCode(code) : '&#x' + (0x10000 + code).toString(16).substring(1) + ';'; } return '>' + chars; }); } function Metadata(meta) { if (typeof meta === 'string') { // Ghostscript produces invalid metadata meta = fixMetadata(meta); var parser = new DOMParser(); meta = parser.parseFromString(meta, 'application/xml'); } else if (!(meta instanceof Document)) { error('Metadata: Invalid metadata object'); } this.metaDocument = meta; this.metadata = {}; this.parse(); } Metadata.prototype = { parse: function Metadata_parse() { var doc = this.metaDocument; var rdf = doc.documentElement; if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in rdf = rdf.firstChild; while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') rdf = rdf.nextSibling; } var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null; if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) return; var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength; for (i = 0, length = children.length; i < length; i++) { desc = children[i]; if (desc.nodeName.toLowerCase() !== 'rdf:description') continue; for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) { if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') { entry = desc.childNodes[ii]; name = entry.nodeName.toLowerCase(); this.metadata[name] = entry.textContent.trim(); } } } }, get: function Metadata_get(name) { return this.metadata[name] || null; }, has: function Metadata_has(name) { return typeof this.metadata[name] !== 'undefined'; } }; return Metadata; })(); // contexts store most of the state we need natively. // However, PDF needs a bit more state, which we store here. // Minimal font size that would be used during canvas fillText operations. var MIN_FONT_SIZE = 16; var COMPILE_TYPE3_GLYPHS = true; function createScratchCanvas(width, height) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; return canvas; } function addContextCurrentTransform(ctx) { // If the context doesn't expose a `mozCurrentTransform`, add a JS based on. if (!ctx.mozCurrentTransform) { // Store the original context ctx._scaleX = ctx._scaleX || 1.0; ctx._scaleY = ctx._scaleY || 1.0; ctx._originalSave = ctx.save; ctx._originalRestore = ctx.restore; ctx._originalRotate = ctx.rotate; ctx._originalScale = ctx.scale; ctx._originalTranslate = ctx.translate; ctx._originalTransform = ctx.transform; ctx._originalSetTransform = ctx.setTransform; ctx._transformMatrix = [ctx._scaleX, 0, 0, ctx._scaleY, 0, 0]; ctx._transformStack = []; Object.defineProperty(ctx, 'mozCurrentTransform', { get: function getCurrentTransform() { return this._transformMatrix; } }); Object.defineProperty(ctx, 'mozCurrentTransformInverse', { get: function getCurrentTransformInverse() { // Calculation done using WolframAlpha: // http://www.wolframalpha.com/input/? // i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}} var m = this._transformMatrix; var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5]; var ad_bc = a * d - b * c; var bc_ad = b * c - a * d; return [ d / ad_bc, b / bc_ad, c / bc_ad, a / ad_bc, (d * e - c * f) / bc_ad, (b * e - a * f) / ad_bc ]; } }); ctx.save = function ctxSave() { var old = this._transformMatrix; this._transformStack.push(old); this._transformMatrix = old.slice(0, 6); this._originalSave(); }; ctx.restore = function ctxRestore() { var prev = this._transformStack.pop(); if (prev) { this._transformMatrix = prev; this._originalRestore(); } }; ctx.translate = function ctxTranslate(x, y) { var m = this._transformMatrix; m[4] = m[0] * x + m[2] * y + m[4]; m[5] = m[1] * x + m[3] * y + m[5]; this._originalTranslate(x, y); }; ctx.scale = function ctxScale(x, y) { var m = this._transformMatrix; m[0] = m[0] * x; m[1] = m[1] * x; m[2] = m[2] * y; m[3] = m[3] * y; this._originalScale(x, y); }; ctx.transform = function ctxTransform(a, b, c, d, e, f) { var m = this._transformMatrix; this._transformMatrix = [ m[0] * a + m[2] * b, m[1] * a + m[3] * b, m[0] * c + m[2] * d, m[1] * c + m[3] * d, m[0] * e + m[2] * f + m[4], m[1] * e + m[3] * f + m[5] ]; ctx._originalTransform(a, b, c, d, e, f); }; ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) { this._transformMatrix = [a, b, c, d, e, f]; ctx._originalSetTransform(a, b, c, d, e, f); }; ctx.rotate = function ctxRotate(angle) { var cosValue = Math.cos(angle); var sinValue = Math.sin(angle); var m = this._transformMatrix; this._transformMatrix = [ m[0] * cosValue + m[2] * sinValue, m[1] * cosValue + m[3] * sinValue, m[0] * (-sinValue) + m[2] * cosValue, m[1] * (-sinValue) + m[3] * cosValue, m[4], m[5] ]; this._originalRotate(angle); }; } } var CachedCanvases = (function CachedCanvasesClosure() { var cache = {}; return { getCanvas: function CachedCanvases_getCanvas(id, width, height, trackTransform) { var canvasEntry; if (id in cache) { canvasEntry = cache[id]; canvasEntry.canvas.width = width; canvasEntry.canvas.height = height; // reset canvas transform for emulated mozCurrentTransform, if needed canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0); } else { var canvas = createScratchCanvas(width, height); var ctx = canvas.getContext('2d'); if (trackTransform) { addContextCurrentTransform(ctx); } cache[id] = canvasEntry = {canvas: canvas, context: ctx}; } return canvasEntry; }, clear: function () { cache = {}; } }; })(); function compileType3Glyph(imgData) { var POINT_TO_PROCESS_LIMIT = 1000; var width = imgData.width, height = imgData.height; var i, j, j0, width1 = width + 1; var points = new Uint8Array(width1 * (height + 1)); var POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); // finding iteresting points: every point is located between mask pixels, // so there will be points of the (width + 1)x(height + 1) grid. Every point // will have flags assigned based on neighboring mask pixels: // 4 | 8 // --P-- // 2 | 1 // We are interested only in points with the flags: // - outside corners: 1, 2, 4, 8; // - inside corners: 7, 11, 13, 14; // - and, intersections: 5, 10. var pos = 3, data = imgData.data, lineSize = width * 4, count = 0; if (data[3] !== 0) { points[0] = 1; ++count; } for (j = 1; j < width; j++) { if (data[pos] !== data[pos + 4]) { points[j] = data[pos] ? 2 : 1; ++count; } pos += 4; } if (data[pos] !== 0) { points[j] = 2; ++count; } pos += 4; for (i = 1; i < height; i++) { j0 = i * width1; if (data[pos - lineSize] !== data[pos]) { points[j0] = data[pos] ? 1 : 8; ++count; } // 'sum' is the position of the current pixel configuration in the 'TYPES' // array (in order 8-1-2-4, so we can use '>>2' to shift the column). var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); for (j = 1; j < width; j++) { sum = (sum >> 2) + (data[pos + 4] ? 4 : 0) + (data[pos - lineSize + 4] ? 8 : 0); if (POINT_TYPES[sum]) { points[j0 + j] = POINT_TYPES[sum]; ++count; } pos += 4; } if (data[pos - lineSize] !== data[pos]) { points[j0 + j] = data[pos] ? 2 : 4; ++count; } pos += 4; if (count > POINT_TO_PROCESS_LIMIT) { return null; } } pos -= lineSize; j0 = i * width1; if (data[pos] !== 0) { points[j0] = 8; ++count; } for (j = 1; j < width; j++) { if (data[pos] !== data[pos + 4]) { points[j0 + j] = data[pos] ? 4 : 8; ++count; } pos += 4; } if (data[pos] !== 0) { points[j0 + j] = 4; ++count; } if (count > POINT_TO_PROCESS_LIMIT) { return null; } // building outlines var steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]); var outlines = []; for (i = 0; count && i <= height; i++) { var p = i * width1; var end = p + width; while (p < end && !points[p]) { p++; } if (p === end) { continue; } var coords = [p % width1, i]; var type = points[p], p0 = p, pp; do { var step = steps[type]; do { p += step; } while (!points[p]); pp = points[p]; if (pp !== 5 && pp !== 10) { // set new direction type = pp; // delete mark points[p] = 0; } else { // type is 5 or 10, ie, a crossing // set new direction type = pp & ((0x33 * type) >> 4); // set new type for "future hit" points[p] &= (type >> 2 | type << 2); } coords.push(p % width1); coords.push((p / width1) | 0); --count; } while (p0 !== p); outlines.push(coords); --i; } var drawOutline = function(c) { c.save(); // the path shall be painted in [0..1]x[0..1] space c.scale(1 / width, -1 / height); c.translate(0, -height); c.beginPath(); for (var i = 0, ii = outlines.length; i < ii; i++) { var o = outlines[i]; c.moveTo(o[0], o[1]); for (var j = 2, jj = o.length; j < jj; j += 2) { c.lineTo(o[j], o[j+1]); } } c.fill(); c.beginPath(); c.restore(); }; return drawOutline; } var CanvasExtraState = (function CanvasExtraStateClosure() { function CanvasExtraState(old) { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; this.fontSize = 0; this.fontSizeScale = 1; this.textMatrix = IDENTITY_MATRIX; this.fontMatrix = FONT_IDENTITY_MATRIX; this.leading = 0; // Current point (in user coordinates) this.x = 0; this.y = 0; // Start of text line (in text coordinates) this.lineX = 0; this.lineY = 0; // Character and word spacing this.charSpacing = 0; this.wordSpacing = 0; this.textHScale = 1; this.textRenderingMode = TextRenderingMode.FILL; this.textRise = 0; // Color spaces this.fillColorSpace = ColorSpace.singletons.gray; this.fillColorSpaceObj = null; this.strokeColorSpace = ColorSpace.singletons.gray; this.strokeColorSpaceObj = null; this.fillColorObj = null; this.strokeColorObj = null; // Default fore and background colors this.fillColor = '#000000'; this.strokeColor = '#000000'; // Note: fill alpha applies to all non-stroking operations this.fillAlpha = 1; this.strokeAlpha = 1; this.lineWidth = 1; this.paintFormXObjectDepth = 0; this.old = old; } CanvasExtraState.prototype = { clone: function CanvasExtraState_clone() { return Object.create(this); }, setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) { this.x = x; this.y = y; } }; return CanvasExtraState; })(); var CanvasGraphics = (function CanvasGraphicsClosure() { // Defines the time the executeOperatorList is going to be executing // before it stops and shedules a continue of execution. var EXECUTION_TIME = 15; function CanvasGraphics(canvasCtx, commonObjs, objs, textLayer, imageLayer) { this.ctx = canvasCtx; this.current = new CanvasExtraState(); this.stateStack = []; this.pendingClip = null; this.pendingEOFill = false; this.res = null; this.xobjs = null; this.commonObjs = commonObjs; this.objs = objs; this.textLayer = textLayer; this.imageLayer = imageLayer; this.groupStack = []; this.processingType3 = null; // Patterns are painted relative to the initial page/form transform, see pdf // spec 8.7.2 NOTE 1. this.baseTransform = null; this.baseTransformStack = []; this.groupLevel = 0; if (canvasCtx) { addContextCurrentTransform(canvasCtx); } } function putBinaryImageData(ctx, imgData) { if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) { ctx.putImageData(imgData, 0, 0); return; } var tmpImgData = ctx.createImageData(imgData.width, imgData.height); var data = imgData.data; var tmpImgDataPixels = tmpImgData.data; if ('set' in tmpImgDataPixels) tmpImgDataPixels.set(data); else { // Copy over the imageData pixel by pixel. for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++) tmpImgDataPixels[i] = data[i]; } ctx.putImageData(tmpImgData, 0, 0); } function copyCtxState(sourceCtx, destCtx) { var properties = ['strokeStyle', 'fillStyle', 'fillRule', 'globalAlpha', 'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', 'globalCompositeOperation', 'font']; for (var i = 0, ii = properties.length; i < ii; i++) { var property = properties[i]; if (property in sourceCtx) { destCtx[property] = sourceCtx[property]; } } if ('setLineDash' in sourceCtx) { destCtx.setLineDash(sourceCtx.getLineDash()); destCtx.lineDashOffset = sourceCtx.lineDashOffset; } else if ('mozDash' in sourceCtx) { destCtx.mozDash = sourceCtx.mozDash; destCtx.mozDashOffset = sourceCtx.mozDashOffset; } } var LINE_CAP_STYLES = ['butt', 'round', 'square']; var LINE_JOIN_STYLES = ['miter', 'round', 'bevel']; var NORMAL_CLIP = {}; var EO_CLIP = {}; CanvasGraphics.prototype = { beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) { // For pdfs that use blend modes we have to clear the canvas else certain // blend modes can look wrong since we'd be blending with a white // backdrop. The problem with a transparent backdrop though is we then // don't get sub pixel anti aliasing on text, so we fill with white if // we can. var width = this.ctx.canvas.width; var height = this.ctx.canvas.height; if (transparency) { this.ctx.clearRect(0, 0, width, height); } else { this.ctx.mozOpaque = true; this.ctx.save(); this.ctx.fillStyle = 'rgb(255, 255, 255)'; this.ctx.fillRect(0, 0, width, height); this.ctx.restore(); } var transform = viewport.transform; this.baseTransform = transform.slice(); this.ctx.save(); this.ctx.transform.apply(this.ctx, transform); if (this.textLayer) { this.textLayer.beginLayout(); } if (this.imageLayer) { this.imageLayer.beginLayout(); } }, executeOperatorList: function CanvasGraphics_executeOperatorList( operatorList, executionStartIdx, continueCallback, stepper) { var argsArray = operatorList.argsArray; var fnArray = operatorList.fnArray; var i = executionStartIdx || 0; var argsArrayLen = argsArray.length; // Sometimes the OperatorList to execute is empty. if (argsArrayLen == i) { return i; } var executionEndIdx; var endTime = Date.now() + EXECUTION_TIME; var commonObjs = this.commonObjs; var objs = this.objs; var fnId; while (true) { if (stepper && i === stepper.nextBreakPoint) { stepper.breakIt(i, continueCallback); return i; } fnId = fnArray[i]; if (fnId !== OPS.dependency) { this[fnId].apply(this, argsArray[i]); } else { var deps = argsArray[i]; for (var n = 0, nn = deps.length; n < nn; n++) { var depObjId = deps[n]; var common = depObjId.substring(0, 2) == 'g_'; // If the promise isn't resolved yet, add the continueCallback // to the promise and bail out. if (!common && !objs.isResolved(depObjId)) { objs.get(depObjId, continueCallback); return i; } if (common && !commonObjs.isResolved(depObjId)) { commonObjs.get(depObjId, continueCallback); return i; } } } i++; // If the entire operatorList was executed, stop as were done. if (i == argsArrayLen) { return i; } // If the execution took longer then a certain amount of time, schedule // to continue exeution after a short delay. // However, this is only possible if a 'continueCallback' is passed in. if (continueCallback && Date.now() > endTime) { setTimeout(continueCallback, 0); return i; } // If the operatorList isn't executed completely yet OR the execution // time was short enough, do another execution round. } }, endDrawing: function CanvasGraphics_endDrawing() { this.ctx.restore(); CachedCanvases.clear(); if (this.textLayer) { this.textLayer.endLayout(); } if (this.imageLayer) { this.imageLayer.endLayout(); } }, // Graphics state setLineWidth: function CanvasGraphics_setLineWidth(width) { this.current.lineWidth = width; this.ctx.lineWidth = width; }, setLineCap: function CanvasGraphics_setLineCap(style) { this.ctx.lineCap = LINE_CAP_STYLES[style]; }, setLineJoin: function CanvasGraphics_setLineJoin(style) { this.ctx.lineJoin = LINE_JOIN_STYLES[style]; }, setMiterLimit: function CanvasGraphics_setMiterLimit(limit) { this.ctx.miterLimit = limit; }, setDash: function CanvasGraphics_setDash(dashArray, dashPhase) { var ctx = this.ctx; if ('setLineDash' in ctx) { ctx.setLineDash(dashArray); ctx.lineDashOffset = dashPhase; } else { ctx.mozDash = dashArray; ctx.mozDashOffset = dashPhase; } }, setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) { // Maybe if we one day fully support color spaces this will be important // for now we can ignore. // TODO set rendering intent? }, setFlatness: function CanvasGraphics_setFlatness(flatness) { // There's no way to control this with canvas, but we can safely ignore. // TODO set flatness? }, setGState: function CanvasGraphics_setGState(states) { for (var i = 0, ii = states.length; i < ii; i++) { var state = states[i]; var key = state[0]; var value = state[1]; switch (key) { case 'LW': this.setLineWidth(value); break; case 'LC': this.setLineCap(value); break; case 'LJ': this.setLineJoin(value); break; case 'ML': this.setMiterLimit(value); break; case 'D': this.setDash(value[0], value[1]); break; case 'RI': this.setRenderingIntent(value); break; case 'FL': this.setFlatness(value); break; case 'Font': this.setFont(value[0], value[1]); break; case 'CA': this.current.strokeAlpha = state[1]; break; case 'ca': this.current.fillAlpha = state[1]; this.ctx.globalAlpha = state[1]; break; case 'BM': if (value && value.name && (value.name !== 'Normal')) { var mode = value.name.replace(/([A-Z])/g, function(c) { return '-' + c.toLowerCase(); } ).substring(1); this.ctx.globalCompositeOperation = mode; if (this.ctx.globalCompositeOperation !== mode) { warn('globalCompositeOperation "' + mode + '" is not supported'); } } else { this.ctx.globalCompositeOperation = 'source-over'; } break; } } }, save: function CanvasGraphics_save() { this.ctx.save(); var old = this.current; this.stateStack.push(old); this.current = old.clone(); }, restore: function CanvasGraphics_restore() { var prev = this.stateStack.pop(); if (prev) { this.current = prev; this.ctx.restore(); } }, transform: function CanvasGraphics_transform(a, b, c, d, e, f) { this.ctx.transform(a, b, c, d, e, f); }, // Path moveTo: function CanvasGraphics_moveTo(x, y) { this.ctx.moveTo(x, y); this.current.setCurrentPoint(x, y); }, lineTo: function CanvasGraphics_lineTo(x, y) { this.ctx.lineTo(x, y); this.current.setCurrentPoint(x, y); }, curveTo: function CanvasGraphics_curveTo(x1, y1, x2, y2, x3, y3) { this.ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); this.current.setCurrentPoint(x3, y3); }, curveTo2: function CanvasGraphics_curveTo2(x2, y2, x3, y3) { var current = this.current; this.ctx.bezierCurveTo(current.x, current.y, x2, y2, x3, y3); current.setCurrentPoint(x3, y3); }, curveTo3: function CanvasGraphics_curveTo3(x1, y1, x3, y3) { this.curveTo(x1, y1, x3, y3, x3, y3); this.current.setCurrentPoint(x3, y3); }, closePath: function CanvasGraphics_closePath() { this.ctx.closePath(); }, rectangle: function CanvasGraphics_rectangle(x, y, width, height) { this.ctx.rect(x, y, width, height); }, stroke: function CanvasGraphics_stroke(consumePath) { consumePath = typeof consumePath !== 'undefined' ? consumePath : true; var ctx = this.ctx; var strokeColor = this.current.strokeColor; if (this.current.lineWidth === 0) ctx.lineWidth = this.getSinglePixelWidth(); // For stroke we want to temporarily change the global alpha to the // stroking alpha. ctx.globalAlpha = this.current.strokeAlpha; if (strokeColor && strokeColor.hasOwnProperty('type') && strokeColor.type === 'Pattern') { // for patterns, we transform to pattern space, calculate // the pattern, call stroke, and restore to user space ctx.save(); ctx.strokeStyle = strokeColor.getPattern(ctx, this); ctx.stroke(); ctx.restore(); } else { ctx.stroke(); } if (consumePath) this.consumePath(); // Restore the global alpha to the fill alpha ctx.globalAlpha = this.current.fillAlpha; }, closeStroke: function CanvasGraphics_closeStroke() { this.closePath(); this.stroke(); }, fill: function CanvasGraphics_fill(consumePath) { consumePath = typeof consumePath !== 'undefined' ? consumePath : true; var ctx = this.ctx; var fillColor = this.current.fillColor; var needRestore = false; if (fillColor && fillColor.hasOwnProperty('type') && fillColor.type === 'Pattern') { ctx.save(); ctx.fillStyle = fillColor.getPattern(ctx, this); needRestore = true; } if (this.pendingEOFill) { if ('mozFillRule' in this.ctx) { this.ctx.mozFillRule = 'evenodd'; this.ctx.fill(); this.ctx.mozFillRule = 'nonzero'; } else { try { this.ctx.fill('evenodd'); } catch (ex) { // shouldn't really happen, but browsers might think differently this.ctx.fill(); } } this.pendingEOFill = false; } else { this.ctx.fill(); } if (needRestore) { ctx.restore(); } if (consumePath) { this.consumePath(); } }, eoFill: function CanvasGraphics_eoFill() { this.pendingEOFill = true; this.fill(); }, fillStroke: function CanvasGraphics_fillStroke() { this.fill(false); this.stroke(false); this.consumePath(); }, eoFillStroke: function CanvasGraphics_eoFillStroke() { this.pendingEOFill = true; this.fillStroke(); }, closeFillStroke: function CanvasGraphics_closeFillStroke() { this.closePath(); this.fillStroke(); }, closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() { this.pendingEOFill = true; this.closePath(); this.fillStroke(); }, endPath: function CanvasGraphics_endPath() { this.consumePath(); }, // Clipping clip: function CanvasGraphics_clip() { this.pendingClip = NORMAL_CLIP; }, eoClip: function CanvasGraphics_eoClip() { this.pendingClip = EO_CLIP; }, // Text beginText: function CanvasGraphics_beginText() { this.current.textMatrix = IDENTITY_MATRIX; this.current.x = this.current.lineX = 0; this.current.y = this.current.lineY = 0; }, endText: function CanvasGraphics_endText() { if (!('pendingTextPaths' in this)) { this.ctx.beginPath(); return; } var paths = this.pendingTextPaths; var ctx = this.ctx; ctx.save(); ctx.beginPath(); for (var i = 0; i < paths.length; i++) { var path = paths[i]; ctx.setTransform.apply(ctx, path.transform); ctx.translate(path.x, path.y); path.addToPath(ctx, path.fontSize); } ctx.restore(); ctx.clip(); ctx.beginPath(); delete this.pendingTextPaths; }, setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) { this.current.charSpacing = spacing; }, setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) { this.current.wordSpacing = spacing; }, setHScale: function CanvasGraphics_setHScale(scale) { this.current.textHScale = scale / 100; }, setLeading: function CanvasGraphics_setLeading(leading) { this.current.leading = -leading; }, setFont: function CanvasGraphics_setFont(fontRefName, size) { var fontObj = this.commonObjs.get(fontRefName); var current = this.current; if (!fontObj) error('Can\'t find font for ' + fontRefName); current.fontMatrix = fontObj.fontMatrix ? fontObj.fontMatrix : FONT_IDENTITY_MATRIX; // A valid matrix needs all main diagonal elements to be non-zero // This also ensures we bypass FF bugzilla bug #719844. if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) { warn('Invalid font matrix for font ' + fontRefName); } // The spec for Tf (setFont) says that 'size' specifies the font 'scale', // and in some docs this can be negative (inverted x-y axes). if (size < 0) { size = -size; current.fontDirection = -1; } else { current.fontDirection = 1; } this.current.font = fontObj; this.current.fontSize = size; if (fontObj.coded) return; // we don't need ctx.font for Type3 fonts var name = fontObj.loadedName || 'sans-serif'; var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : (fontObj.bold ? 'bold' : 'normal'); var italic = fontObj.italic ? 'italic' : 'normal'; var typeface = '"' + name + '", ' + fontObj.fallbackName; // Some font backends cannot handle fonts below certain size. // Keeping the font at minimal size and using the fontSizeScale to change // the current transformation matrix before the fillText/strokeText. // See https://bugzilla.mozilla.org/show_bug.cgi?id=726227 var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE; this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 : size / MIN_FONT_SIZE; var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface; this.ctx.font = rule; }, setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) { this.current.textRenderingMode = mode; }, setTextRise: function CanvasGraphics_setTextRise(rise) { this.current.textRise = rise; }, moveText: function CanvasGraphics_moveText(x, y) { this.current.x = this.current.lineX += x; this.current.y = this.current.lineY += y; }, setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) { this.setLeading(-y); this.moveText(x, y); }, setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) { this.current.textMatrix = [a, b, c, d, e, f]; this.current.x = this.current.lineX = 0; this.current.y = this.current.lineY = 0; }, nextLine: function CanvasGraphics_nextLine() { this.moveText(0, this.current.leading); }, applyTextTransforms: function CanvasGraphics_applyTextTransforms() { var ctx = this.ctx; var current = this.current; ctx.transform.apply(ctx, current.textMatrix); ctx.translate(current.x, current.y + current.textRise); if (current.fontDirection > 0) { ctx.scale(current.textHScale, -1); } else { ctx.scale(-current.textHScale, 1); } }, createTextGeometry: function CanvasGraphics_createTextGeometry() { var geometry = {}; var ctx = this.ctx; var font = this.current.font; var ctxMatrix = ctx.mozCurrentTransform; var a = ctxMatrix[0], b = ctxMatrix[1], c = ctxMatrix[2]; var d = ctxMatrix[3], e = ctxMatrix[4], f = ctxMatrix[5]; var sx = (a >= 0) ? Math.sqrt((a * a) + (b * b)) : -Math.sqrt((a * a) + (b * b)); var sy = (d >= 0) ? Math.sqrt((c * c) + (d * d)) : -Math.sqrt((c * c) + (d * d)); var angle = Math.atan2(b, a); var x = e; var y = f; geometry.x = x; geometry.y = y; geometry.hScale = sx; geometry.vScale = sy; geometry.angle = angle; geometry.spaceWidth = font.spaceWidth; geometry.fontName = font.loadedName; geometry.fontFamily = font.fallbackName; geometry.fontSize = this.current.fontSize; return geometry; }, paintChar: function (character, x, y) { var ctx = this.ctx; var current = this.current; var font = current.font; var fontSize = current.fontSize / current.fontSizeScale; var textRenderingMode = current.textRenderingMode; var fillStrokeMode = textRenderingMode & TextRenderingMode.FILL_STROKE_MASK; var isAddToPathSet = !!(textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); var addToPath; if (font.disableFontFace || isAddToPathSet) { addToPath = font.getPathGenerator(this.commonObjs, character); } if (font.disableFontFace) { ctx.save(); ctx.translate(x, y); ctx.beginPath(); addToPath(ctx, fontSize); if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { ctx.fill(); } if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { ctx.stroke(); } ctx.restore(); } else { if (fillStrokeMode === TextRenderingMode.FILL || fillStrokeMode === TextRenderingMode.FILL_STROKE) { ctx.fillText(character, x, y); } if (fillStrokeMode === TextRenderingMode.STROKE || fillStrokeMode === TextRenderingMode.FILL_STROKE) { ctx.strokeText(character, x, y); } } if (isAddToPathSet) { var paths = this.pendingTextPaths || (this.pendingTextPaths = []); paths.push({ transform: ctx.mozCurrentTransform, x: x, y: y, fontSize: fontSize, addToPath: addToPath }); } }, showText: function CanvasGraphics_showText(glyphs, skipTextSelection) { var ctx = this.ctx; var current = this.current; var font = current.font; var fontSize = current.fontSize; var fontSizeScale = current.fontSizeScale; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; var textHScale = current.textHScale * current.fontDirection; var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; var glyphsLength = glyphs.length; var textLayer = this.textLayer; var geom; var textSelection = textLayer && !skipTextSelection ? true : false; var canvasWidth = 0.0; var vertical = font.vertical; var defaultVMetrics = font.defaultVMetrics; // Type3 fonts - each glyph is a "mini-PDF" if (font.coded) { ctx.save(); ctx.transform.apply(ctx, current.textMatrix); ctx.translate(current.x, current.y); ctx.scale(textHScale, 1); if (textSelection) { this.save(); ctx.scale(1, -1); geom = this.createTextGeometry(); this.restore(); } for (var i = 0; i < glyphsLength; ++i) { var glyph = glyphs[i]; if (glyph === null) { // word break this.ctx.translate(wordSpacing, 0); current.x += wordSpacing * textHScale; continue; } this.processingType3 = glyph; this.save(); ctx.scale(fontSize, fontSize); ctx.transform.apply(ctx, fontMatrix); this.executeOperatorList(glyph.operatorList); this.restore(); var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); var width = (transformed[0] * fontSize + charSpacing) * current.fontDirection; ctx.translate(width, 0); current.x += width * textHScale; canvasWidth += width; } ctx.restore(); this.processingType3 = null; } else { ctx.save(); this.applyTextTransforms(); var lineWidth = current.lineWidth; var a1 = current.textMatrix[0], b1 = current.textMatrix[1]; var scale = Math.sqrt(a1 * a1 + b1 * b1); if (scale === 0 || lineWidth === 0) lineWidth = this.getSinglePixelWidth(); else lineWidth /= scale; if (textSelection) geom = this.createTextGeometry(); if (fontSizeScale != 1.0) { ctx.scale(fontSizeScale, fontSizeScale); lineWidth /= fontSizeScale; } ctx.lineWidth = lineWidth; var x = 0; for (var i = 0; i < glyphsLength; ++i) { var glyph = glyphs[i]; if (glyph === null) { // word break x += current.fontDirection * wordSpacing; continue; } var restoreNeeded = false; var character = glyph.fontChar; var vmetric = glyph.vmetric || defaultVMetrics; if (vertical) { var vx = glyph.vmetric ? vmetric[1] : glyph.width * 0.5; vx = -vx * fontSize * current.fontMatrix[0]; var vy = vmetric[2] * fontSize * current.fontMatrix[0]; } var width = vmetric ? -vmetric[0] : glyph.width; var charWidth = width * fontSize * current.fontMatrix[0] + charSpacing * current.fontDirection; var accent = glyph.accent; var scaledX, scaledY, scaledAccentX, scaledAccentY; if (!glyph.disabled) { if (vertical) { scaledX = vx / fontSizeScale; scaledY = (x + vy) / fontSizeScale; } else { scaledX = x / fontSizeScale; scaledY = 0; } if (font.remeasure && width > 0) { // some standard fonts may not have the exact width, trying to // rescale per character var measuredWidth = ctx.measureText(character).width * 1000 / current.fontSize * current.fontSizeScale; var characterScaleX = width / measuredWidth; restoreNeeded = true; ctx.save(); ctx.scale(characterScaleX, 1); scaledX /= characterScaleX; if (accent) { scaledAccentX /= characterScaleX; } } this.paintChar(character, scaledX, scaledY); if (accent) { scaledAccentX = scaledX + accent.offset.x / fontSizeScale; scaledAccentY = scaledY - accent.offset.y / fontSizeScale; this.paintChar(accent.fontChar, scaledAccentX, scaledAccentY); } } x += charWidth; canvasWidth += charWidth; if (restoreNeeded) { ctx.restore(); } } if (vertical) { current.y -= x * textHScale; } else { current.x += x * textHScale; } ctx.restore(); } if (textSelection) { geom.canvasWidth = canvasWidth; if (vertical) { var VERTICAL_TEXT_ROTATION = Math.PI / 2; geom.angle += VERTICAL_TEXT_ROTATION; } this.textLayer.appendText(geom); } return canvasWidth; }, showSpacedText: function CanvasGraphics_showSpacedText(arr) { var ctx = this.ctx; var current = this.current; var font = current.font; var fontSize = current.fontSize; // TJ array's number is independent from fontMatrix var textHScale = current.textHScale * 0.001 * current.fontDirection; var arrLength = arr.length; var textLayer = this.textLayer; var geom; var canvasWidth = 0.0; var textSelection = textLayer ? true : false; var vertical = font.vertical; var spacingAccumulator = 0; if (textSelection) { ctx.save(); this.applyTextTransforms(); geom = this.createTextGeometry(); ctx.restore(); } for (var i = 0; i < arrLength; ++i) { var e = arr[i]; if (isNum(e)) { var spacingLength = -e * fontSize * textHScale; if (vertical) { current.y += spacingLength; } else { current.x += spacingLength; } if (textSelection) spacingAccumulator += spacingLength; } else { var shownCanvasWidth = this.showText(e, true); if (textSelection) { canvasWidth += spacingAccumulator + shownCanvasWidth; spacingAccumulator = 0; } } } if (textSelection) { geom.canvasWidth = canvasWidth; if (vertical) { var VERTICAL_TEXT_ROTATION = Math.PI / 2; geom.angle += VERTICAL_TEXT_ROTATION; } this.textLayer.appendText(geom); } }, nextLineShowText: function CanvasGraphics_nextLineShowText(text) { this.nextLine(); this.showText(text); }, nextLineSetSpacingShowText: function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing, charSpacing, text) { this.setWordSpacing(wordSpacing); this.setCharSpacing(charSpacing); this.nextLineShowText(text); }, // Type3 fonts setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) { // We can safely ignore this since the width should be the same // as the width in the Widths array. }, setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) { // TODO According to the spec we're also suppose to ignore any operators // that set color or include images while processing this type3 font. this.rectangle(llx, lly, urx - llx, ury - lly); this.clip(); this.endPath(); }, // Color setStrokeColorSpace: function CanvasGraphics_setStrokeColorSpace(raw) { this.current.strokeColorSpace = ColorSpace.fromIR(raw); }, setFillColorSpace: function CanvasGraphics_setFillColorSpace(raw) { this.current.fillColorSpace = ColorSpace.fromIR(raw); }, setStrokeColor: function CanvasGraphics_setStrokeColor(/*...*/) { var cs = this.current.strokeColorSpace; var rgbColor = cs.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.strokeStyle = color; this.current.strokeColor = color; }, getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) { if (IR[0] == 'TilingPattern') { var args = IR[1]; var base = cs.base; var color; if (base) { var baseComps = base.numComps; color = base.getRgb(args, 0); } var pattern = new TilingPattern(IR, color, this.ctx, this.objs, this.commonObjs, this.baseTransform); } else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') { var pattern = Pattern.shadingFromIR(IR); } else { error('Unkown IR type ' + IR[0]); } return pattern; }, setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) { var cs = this.current.strokeColorSpace; if (cs.name == 'Pattern') { this.current.strokeColor = this.getColorN_Pattern(arguments, cs); } else { this.setStrokeColor.apply(this, arguments); } }, setFillColor: function CanvasGraphics_setFillColor(/*...*/) { var cs = this.current.fillColorSpace; var rgbColor = cs.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.fillStyle = color; this.current.fillColor = color; }, setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) { var cs = this.current.fillColorSpace; if (cs.name == 'Pattern') { this.current.fillColor = this.getColorN_Pattern(arguments, cs); } else { this.setFillColor.apply(this, arguments); } }, setStrokeGray: function CanvasGraphics_setStrokeGray(gray) { this.current.strokeColorSpace = ColorSpace.singletons.gray; var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.strokeStyle = color; this.current.strokeColor = color; }, setFillGray: function CanvasGraphics_setFillGray(gray) { this.current.fillColorSpace = ColorSpace.singletons.gray; var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.fillStyle = color; this.current.fillColor = color; }, setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) { this.current.strokeColorSpace = ColorSpace.singletons.rgb; var rgbColor = this.current.strokeColorSpace.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.strokeStyle = color; this.current.strokeColor = color; }, setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) { this.current.fillColorSpace = ColorSpace.singletons.rgb; var rgbColor = this.current.fillColorSpace.getRgb(arguments, 0); var color = Util.makeCssRgb(rgbColor); this.ctx.fillStyle = color; this.current.fillColor = color; }, setStrokeCMYKColor: function CanvasGraphics_setStrokeCMYKColor(c, m, y, k) { this.current.strokeColorSpace = ColorSpace.singletons.cmyk; var color = Util.makeCssCmyk(arguments); this.ctx.strokeStyle = color; this.current.strokeColor = color; }, setFillCMYKColor: function CanvasGraphics_setFillCMYKColor(c, m, y, k) { this.current.fillColorSpace = ColorSpace.singletons.cmyk; var color = Util.makeCssCmyk(arguments); this.ctx.fillStyle = color; this.current.fillColor = color; }, shadingFill: function CanvasGraphics_shadingFill(patternIR) { var ctx = this.ctx; this.save(); var pattern = Pattern.shadingFromIR(patternIR); ctx.fillStyle = pattern.getPattern(ctx, this); var inv = ctx.mozCurrentTransformInverse; if (inv) { var canvas = ctx.canvas; var width = canvas.width; var height = canvas.height; var bl = Util.applyTransform([0, 0], inv); var br = Util.applyTransform([0, height], inv); var ul = Util.applyTransform([width, 0], inv); var ur = Util.applyTransform([width, height], inv); var x0 = Math.min(bl[0], br[0], ul[0], ur[0]); var y0 = Math.min(bl[1], br[1], ul[1], ur[1]); var x1 = Math.max(bl[0], br[0], ul[0], ur[0]); var y1 = Math.max(bl[1], br[1], ul[1], ur[1]); this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0); } else { // HACK to draw the gradient onto an infinite rectangle. // PDF gradients are drawn across the entire image while // Canvas only allows gradients to be drawn in a rectangle // The following bug should allow us to remove this. // https://bugzilla.mozilla.org/show_bug.cgi?id=664884 this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10); } this.restore(); }, // Images beginInlineImage: function CanvasGraphics_beginInlineImage() { error('Should not call beginInlineImage'); }, beginImageData: function CanvasGraphics_beginImageData() { error('Should not call beginImageData'); }, paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix, bbox) { this.save(); this.current.paintFormXObjectDepth++; this.baseTransformStack.push(this.baseTransform); if (matrix && isArray(matrix) && 6 == matrix.length) this.transform.apply(this, matrix); this.baseTransform = this.ctx.mozCurrentTransform; if (bbox && isArray(bbox) && 4 == bbox.length) { var width = bbox[2] - bbox[0]; var height = bbox[3] - bbox[1]; this.rectangle(bbox[0], bbox[1], width, height); this.clip(); this.endPath(); } }, paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() { var depth = this.current.paintFormXObjectDepth; do { this.restore(); // some pdf don't close all restores inside object // closing those for them } while (this.current.paintFormXObjectDepth >= depth); this.baseTransform = this.baseTransformStack.pop(); }, beginGroup: function CanvasGraphics_beginGroup(group) { this.save(); var currentCtx = this.ctx; // TODO non-isolated groups - according to Rik at adobe non-isolated // group results aren't usually that different and they even have tools // that ignore this setting. Notes from Rik on implmenting: // - When you encounter an transparency group, create a new canvas with // the dimensions of the bbox // - copy the content from the previous canvas to the new canvas // - draw as usual // - remove the backdrop alpha: // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha // value of your transparency group and 'alphaBackdrop' the alpha of the // backdrop // - remove background color: // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew) if (!group.isolated) { info('TODO: Support non-isolated groups.'); } // TODO knockout - supposedly possible with the clever use of compositing // modes. if (group.knockout) { TODO('Support knockout groups.'); } var currentTransform = currentCtx.mozCurrentTransform; if (group.matrix) { currentCtx.transform.apply(currentCtx, group.matrix); } assert(group.bbox, 'Bounding box is required.'); // Based on the current transform figure out how big the bounding box // will actually be. var bounds = Util.getAxialAlignedBoundingBox( group.bbox, currentCtx.mozCurrentTransform); // Clip the bounding box to the current canvas. var canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height]; bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; // Use ceil in case we're between sizes so we don't create canvas that is // too small and make the canvas at least 1x1 pixels. var drawnWidth = Math.max(Math.ceil(bounds[2] - bounds[0]), 1); var drawnHeight = Math.max(Math.ceil(bounds[3] - bounds[1]), 1); var scratchCanvas = CachedCanvases.getCanvas( 'groupAt' + this.groupLevel, drawnWidth, drawnHeight, true); var groupCtx = scratchCanvas.context; // Since we created a new canvas that is just the size of the bounding box // we have to translate the group ctx. var offsetX = bounds[0]; var offsetY = bounds[1]; groupCtx.translate(-offsetX, -offsetY); groupCtx.transform.apply(groupCtx, currentTransform); // Setup the current ctx so when the group is popped we draw it the right // location. currentCtx.setTransform(1, 0, 0, 1, 0, 0); currentCtx.translate(offsetX, offsetY); // The transparency group inherits all off the current graphics state // except the blend mode, soft mask, and alpha constants. copyCtxState(currentCtx, groupCtx); this.ctx = groupCtx; this.setGState([ ['SMask', 'None'], ['BM', 'Normal'], ['ca', 1], ['CA', 1] ]); this.groupStack.push(currentCtx); this.groupLevel++; }, endGroup: function CanvasGraphics_endGroup(group) { this.groupLevel--; var groupCtx = this.ctx; this.ctx = this.groupStack.pop(); // Turn off image smoothing to avoid sub pixel interpolation which can // look kind of blurry for some pdfs. if ('imageSmoothingEnabled' in this.ctx) { this.ctx.imageSmoothingEnabled = false; } else { this.ctx.mozImageSmoothingEnabled = false; } this.ctx.drawImage(groupCtx.canvas, 0, 0); this.restore(); }, beginAnnotations: function CanvasGraphics_beginAnnotations() { this.save(); this.current = new CanvasExtraState(); }, endAnnotations: function CanvasGraphics_endAnnotations() { this.restore(); }, beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform, matrix) { this.save(); if (rect && isArray(rect) && 4 == rect.length) { var width = rect[2] - rect[0]; var height = rect[3] - rect[1]; this.rectangle(rect[0], rect[1], width, height); this.clip(); this.endPath(); } this.transform.apply(this, transform); this.transform.apply(this, matrix); }, endAnnotation: function CanvasGraphics_endAnnotation() { this.restore(); }, paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) { var domImage = this.objs.get(objId); if (!domImage) { error('Dependent image isn\'t ready yet'); } this.save(); var ctx = this.ctx; // scale the image to the unit square ctx.scale(1 / w, -1 / h); ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height, 0, -h, w, h); if (this.imageLayer) { var currentTransform = ctx.mozCurrentTransformInverse; var position = this.getCanvasPosition(0, 0); this.imageLayer.appendImage({ objId: objId, left: position[0], top: position[1], width: w / currentTransform[0], height: h / currentTransform[3] }); } this.restore(); }, paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(img) { var ctx = this.ctx; var width = img.width, height = img.height; var glyph = this.processingType3; if (COMPILE_TYPE3_GLYPHS && glyph && !('compiled' in glyph)) { var MAX_SIZE_TO_COMPILE = 1000; if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) { glyph.compiled = compileType3Glyph({data: img.data, width: width, height: height}); } else { glyph.compiled = null; } } if (glyph && glyph.compiled) { glyph.compiled(ctx); return; } var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); var maskCtx = maskCanvas.context; maskCtx.save(); putBinaryImageData(maskCtx, img); maskCtx.globalCompositeOperation = 'source-in'; var fillColor = this.current.fillColor; maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') && fillColor.type === 'Pattern') ? fillColor.getPattern(maskCtx, this) : fillColor; maskCtx.fillRect(0, 0, width, height); maskCtx.restore(); this.paintInlineImageXObject(maskCanvas.canvas); }, paintImageMaskXObjectGroup: function CanvasGraphics_paintImageMaskXObjectGroup(images) { var ctx = this.ctx; for (var i = 0, ii = images.length; i < ii; i++) { var image = images[i]; var width = image.width, height = image.height; var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); var maskCtx = maskCanvas.context; maskCtx.save(); putBinaryImageData(maskCtx, image); maskCtx.globalCompositeOperation = 'source-in'; var fillColor = this.current.fillColor; maskCtx.fillStyle = (fillColor && fillColor.hasOwnProperty('type') && fillColor.type === 'Pattern') ? fillColor.getPattern(maskCtx, this) : fillColor; maskCtx.fillRect(0, 0, width, height); maskCtx.restore(); ctx.save(); ctx.transform.apply(ctx, image.transform); ctx.scale(1, -1); ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); ctx.restore(); } }, paintImageXObject: function CanvasGraphics_paintImageXObject(objId) { var imgData = this.objs.get(objId); if (!imgData) error('Dependent image isn\'t ready yet'); this.paintInlineImageXObject(imgData); }, paintInlineImageXObject: function CanvasGraphics_paintInlineImageXObject(imgData) { var width = imgData.width; var height = imgData.height; var ctx = this.ctx; this.save(); // scale the image to the unit square ctx.scale(1 / width, -1 / height); var currentTransform = ctx.mozCurrentTransformInverse; var a = currentTransform[0], b = currentTransform[1]; var widthScale = Math.max(Math.sqrt(a * a + b * b), 1); var c = currentTransform[2], d = currentTransform[3]; var heightScale = Math.max(Math.sqrt(c * c + d * d), 1); var imgToPaint; // instanceof HTMLElement does not work in jsdom node.js module if (imgData instanceof HTMLElement || !imgData.data) { imgToPaint = imgData; } else { var tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height); var tmpCtx = tmpCanvas.context; putBinaryImageData(tmpCtx, imgData); imgToPaint = tmpCanvas.canvas; } var paintWidth = width, paintHeight = height; var tmpCanvasId = 'prescale1'; // Vertial or horizontal scaling shall not be more than 2 to not loose the // pixels during drawImage operation, painting on the temporary canvas(es) // that are twice smaller in size while ((widthScale > 2 && paintWidth > 1) || (heightScale > 2 && paintHeight > 1)) { var newWidth = paintWidth, newHeight = paintHeight; if (widthScale > 2 && paintWidth > 1) { newWidth = Math.ceil(paintWidth / 2); widthScale /= paintWidth / newWidth; } if (heightScale > 2 && paintHeight > 1) { newHeight = Math.ceil(paintHeight / 2); heightScale /= paintHeight / newHeight; } var tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); tmpCtx = tmpCanvas.context; tmpCtx.clearRect(0, 0, newWidth, newHeight); tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); imgToPaint = tmpCanvas.canvas; paintWidth = newWidth; paintHeight = newHeight; tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1'; } ctx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, -height, width, height); if (this.imageLayer) { var position = this.getCanvasPosition(0, -height); this.imageLayer.appendImage({ imgData: imgData, left: position[0], top: position[1], width: width / currentTransform[0], height: height / currentTransform[3] }); } this.restore(); }, paintInlineImageXObjectGroup: function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) { var ctx = this.ctx; var w = imgData.width; var h = imgData.height; var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h); var tmpCtx = tmpCanvas.context; putBinaryImageData(tmpCtx, imgData); for (var i = 0, ii = map.length; i < ii; i++) { var entry = map[i]; ctx.save(); ctx.transform.apply(ctx, entry.transform); ctx.scale(1, -1); ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); if (this.imageLayer) { var position = this.getCanvasPosition(entry.x, entry.y); this.imageLayer.appendImage({ imgData: imgData, left: position[0], top: position[1], width: w, height: h }); } ctx.restore(); } }, // Marked content markPoint: function CanvasGraphics_markPoint(tag) { // TODO Marked content. }, markPointProps: function CanvasGraphics_markPointProps(tag, properties) { // TODO Marked content. }, beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) { // TODO Marked content. }, beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps( tag, properties) { // TODO Marked content. }, endMarkedContent: function CanvasGraphics_endMarkedContent() { // TODO Marked content. }, // Compatibility beginCompat: function CanvasGraphics_beginCompat() { // TODO ignore undefined operators (should we do that anyway?) }, endCompat: function CanvasGraphics_endCompat() { // TODO stop ignoring undefined operators }, // Helper functions consumePath: function CanvasGraphics_consumePath() { if (this.pendingClip) { if (this.pendingClip == EO_CLIP) { if ('mozFillRule' in this.ctx) { this.ctx.mozFillRule = 'evenodd'; this.ctx.clip(); this.ctx.mozFillRule = 'nonzero'; } else { try { this.ctx.clip('evenodd'); } catch (ex) { // shouldn't really happen, but browsers might think differently this.ctx.clip(); } } } else { this.ctx.clip(); } this.pendingClip = null; } this.ctx.beginPath(); }, getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) { var inverse = this.ctx.mozCurrentTransformInverse; // max of the current horizontal and vertical scale return Math.sqrt(Math.max( (inverse[0] * inverse[0] + inverse[1] * inverse[1]), (inverse[2] * inverse[2] + inverse[3] * inverse[3]))); }, getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) { var transform = this.ctx.mozCurrentTransform; return [ transform[0] * x + transform[2] * y + transform[4], transform[1] * x + transform[3] * y + transform[5] ]; } }; for (var op in OPS) { CanvasGraphics.prototype[OPS[op]] = CanvasGraphics.prototype[op]; } return CanvasGraphics; })(); PDFJS.disableFontFace = false; var FontLoader = { insertRule: function fontLoaderInsertRule(rule) { var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); if (!styleElement) { styleElement = document.createElement('style'); styleElement.id = 'PDFJS_FONT_STYLE_TAG'; document.documentElement.getElementsByTagName('head')[0].appendChild( styleElement); } var styleSheet = styleElement.sheet; styleSheet.insertRule(rule, styleSheet.cssRules.length); }, clear: function fontLoaderClear() { var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG'); if (styleElement) { styleElement.parentNode.removeChild(styleElement); } }, get loadTestFont() { // This is a CFF font with 1 glyph for '.' that fills its entire width and // height. return shadow(this, 'loadTestFont', atob( 'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' + 'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' + 'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' + 'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' + 'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' + 'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' + 'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' + 'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' + 'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' + 'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' + 'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' + 'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' + 'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' + 'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' + 'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' + 'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' + 'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' + 'ABAAAAAAAAAAAD6AAAAAAAAA==' )); }, loadTestFontId: 0, loadingContext: { requests: [], nextRequestId: 0 }, isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() { if (isWorker) return false; // User agent string sniffing is bad, but there is no reliable way to tell // if font is fully loaded and ready to be used with canvas. var userAgent = window.navigator.userAgent; var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent); if (m && m[1] >= 14) return true; // TODO other browsers return false; })(), bind: function fontLoaderBind(fonts, callback) { assert(!isWorker, 'bind() shall be called from main thread'); var rules = [], fontsToLoad = []; for (var i = 0, ii = fonts.length; i < ii; i++) { var font = fonts[i]; // Add the font to the DOM only once or skip if the font // is already loaded. if (font.attached || font.loading === false) { continue; } font.attached = true; var rule = font.bindDOM(); if (rule) { rules.push(rule); fontsToLoad.push(font); } } var request = FontLoader.queueLoadingCallback(callback); if (rules.length > 0 && !this.isSyncFontLoadingSupported) { FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request); } else { request.complete(); } }, queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) { function LoadLoader_completeRequest() { assert(!request.end, 'completeRequest() cannot be called twice'); request.end = Date.now(); // sending all completed requests in order how they were queued while (context.requests.length > 0 && context.requests[0].end) { var otherRequest = context.requests.shift(); setTimeout(otherRequest.callback, 0); } } var context = FontLoader.loadingContext; var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++); var request = { id: requestId, complete: LoadLoader_completeRequest, callback: callback, started: Date.now() }; context.requests.push(request); return request; }, prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, fonts, request) { /** Hack begin */ // There's currently no event when a font has finished downloading so the // following code is a dirty hack to 'guess' when a font is // ready. It's assumed fonts are loaded in order, so add a known test // font after the desired fonts and then test for the loading of that // test font. function int32(data, offset) { return (data.charCodeAt(offset) << 24) | (data.charCodeAt(offset + 1) << 16) | (data.charCodeAt(offset + 2) << 8) | (data.charCodeAt(offset + 3) & 0xff); } function string32(value) { return String.fromCharCode((value >> 24) & 0xff) + String.fromCharCode((value >> 16) & 0xff) + String.fromCharCode((value >> 8) & 0xff) + String.fromCharCode(value & 0xff); } function spliceString(s, offset, remove, insert) { var chunk1 = data.substr(0, offset); var chunk2 = data.substr(offset + remove); return chunk1 + insert + chunk2; } var i, ii; var canvas = document.createElement('canvas'); canvas.width = 1; canvas.height = 1; var ctx = canvas.getContext('2d'); var called = 0; function isFontReady(name, callback) { called++; // With setTimeout clamping this gives the font ~100ms to load. if(called > 30) { warn('Load test font never loaded.'); callback(); return; } ctx.font = '30px ' + name; ctx.fillText('.', 0, 20); var imageData = ctx.getImageData(0, 0, 1, 1); if (imageData.data[3] > 0) { callback(); return; } setTimeout(isFontReady.bind(null, name, callback)); } var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++; // Chromium seems to cache fonts based on a hash of the actual font data, // so the font must be modified for each load test else it will appear to // be loaded already. // TODO: This could maybe be made faster by avoiding the btoa of the full // font by splitting it in chunks before hand and padding the font id. var data = this.loadTestFont; var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum) data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length, loadTestFontId); // CFF checksum is important for IE, adjusting it var CFF_CHECKSUM_OFFSET = 16; var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X' var checksum = int32(data, CFF_CHECKSUM_OFFSET); for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) { checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0; } if (i < loadTestFontId.length) { // align to 4 bytes boundary checksum = (checksum - XXXX_VALUE + int32(loadTestFontId + 'XXX', i)) | 0; } data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum)); var url = 'url(data:font/opentype;base64,' + btoa(data) + ');'; var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' + url + '}'; FontLoader.insertRule(rule); var names = []; for (i = 0, ii = fonts.length; i < ii; i++) { names.push(fonts[i].loadedName); } names.push(loadTestFontId); var div = document.createElement('div'); div.setAttribute('style', 'visibility: hidden;' + 'width: 10px; height: 10px;' + 'position: absolute; top: 0px; left: 0px;'); for (i = 0, ii = names.length; i < ii; ++i) { var span = document.createElement('span'); span.textContent = 'Hi'; span.style.fontFamily = names[i]; div.appendChild(span); } document.body.appendChild(div); isFontReady(loadTestFontId, function() { document.body.removeChild(div); request.complete(); }); /** Hack end */ } }; var FontFace = (function FontFaceClosure() { function FontFace(name, file, properties) { this.compiledGlyphs = {}; if (arguments.length === 1) { // importing translated data var data = arguments[0]; for (var i in data) { this[i] = data[i]; } return; } } FontFace.prototype = { bindDOM: function FontFace_bindDOM() { if (!this.data) return null; if (PDFJS.disableFontFace) { this.disableFontFace = true; return null; } var data = bytesToString(this.data); var fontName = this.loadedName; // Add the font-face rule to the document var url = ('url(data:' + this.mimetype + ';base64,' + window.btoa(data) + ');'); var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}'; FontLoader.insertRule(rule); if (PDFJS.pdfBug && 'FontInspector' in globalScope && globalScope['FontInspector'].enabled) globalScope['FontInspector'].fontAdded(this, url); return rule; }, getPathGenerator: function (objs, character) { if (!(character in this.compiledGlyphs)) { var js = objs.get(this.loadedName + '_path_' + character); /*jshint -W054 */ this.compiledGlyphs[character] = new Function('c', 'size', js); } return this.compiledGlyphs[character]; } }; return FontFace; })(); }).call((typeof window === 'undefined') ? this : window); if (!PDFJS.workerSrc && typeof document !== 'undefined') { // workerSrc is not set -- using last script url to define default location PDFJS.workerSrc = (function () { 'use strict'; var scriptTagContainer = document.body || document.getElementsByTagName('head')[0]; var pdfjsSrc = scriptTagContainer.lastChild.src; return pdfjsSrc && pdfjsSrc.replace(/\.js$/i, '.worker.js'); })(); } /* 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. */ /* globals VBArray, PDFJS */ 'use strict'; // Initializing PDFJS global object here, it case if we need to change/disable // some PDF.js features, e.g. range requests if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } // Checking if the typed arrays are supported // Support: iOS<6.0 (subarray), IE<10, Android<4.0 (function checkTypedArrayCompatibility() { if (typeof Uint8Array !== 'undefined') { // Support: iOS<6.0 if (typeof Uint8Array.prototype.subarray === 'undefined') { Uint8Array.prototype.subarray = function subarray(start, end) { return new Uint8Array(this.slice(start, end)); }; Float32Array.prototype.subarray = function subarray(start, end) { return new Float32Array(this.slice(start, end)); }; } // Support: Android<4.1 if (typeof Float64Array === 'undefined') { window.Float64Array = Float32Array; } return; } function subarray(start, end) { return new TypedArray(this.slice(start, end)); } function setArrayOffset(array, offset) { if (arguments.length < 2) { offset = 0; } for (var i = 0, n = array.length; i < n; ++i, ++offset) { this[offset] = array[i] & 0xFF; } } function TypedArray(arg1) { var result, i, n; if (typeof arg1 === 'number') { result = []; for (i = 0; i < arg1; ++i) { result[i] = 0; } } else if ('slice' in arg1) { result = arg1.slice(0); } else { result = []; for (i = 0, n = arg1.length; i < n; ++i) { result[i] = arg1[i]; } } result.subarray = subarray; result.buffer = result; result.byteLength = result.length; result.set = setArrayOffset; if (typeof arg1 === 'object' && arg1.buffer) { result.buffer = arg1.buffer; } return result; } window.Uint8Array = TypedArray; window.Int8Array = TypedArray; // we don't need support for set, byteLength for 32-bit array // so we can use the TypedArray as well window.Uint32Array = TypedArray; window.Int32Array = TypedArray; window.Uint16Array = TypedArray; window.Float32Array = TypedArray; window.Float64Array = TypedArray; })(); // URL = URL || webkitURL // Support: Safari<7, Android 4.2+ (function normalizeURLObject() { if (!window.URL) { window.URL = window.webkitURL; } })(); // Object.defineProperty()? // Support: Android<4.0, Safari<5.1 (function checkObjectDefinePropertyCompatibility() { if (typeof Object.defineProperty !== 'undefined') { var definePropertyPossible = true; try { // some browsers (e.g. safari) cannot use defineProperty() on DOM objects // and thus the native version is not sufficient Object.defineProperty(new Image(), 'id', { value: 'test' }); // ... another test for android gb browser for non-DOM objects var Test = function Test() {}; Test.prototype = { get id() { } }; Object.defineProperty(new Test(), 'id', { value: '', configurable: true, enumerable: true, writable: false }); } catch (e) { definePropertyPossible = false; } if (definePropertyPossible) { return; } } Object.defineProperty = function objectDefineProperty(obj, name, def) { delete obj[name]; if ('get' in def) { obj.__defineGetter__(name, def['get']); } if ('set' in def) { obj.__defineSetter__(name, def['set']); } if ('value' in def) { obj.__defineSetter__(name, function objectDefinePropertySetter(value) { this.__defineGetter__(name, function objectDefinePropertyGetter() { return value; }); return value; }); obj[name] = def.value; } }; })(); // No XMLHttpRequest#response? // Support: IE<11, Android <4.0 (function checkXMLHttpRequestResponseCompatibility() { var xhrPrototype = XMLHttpRequest.prototype; var xhr = new XMLHttpRequest(); if (!('overrideMimeType' in xhr)) { // IE10 might have response, but not overrideMimeType // Support: IE10 Object.defineProperty(xhrPrototype, 'overrideMimeType', { value: function xmlHttpRequestOverrideMimeType(mimeType) {} }); } if ('responseType' in xhr) { return; } // The worker will be using XHR, so we can save time and disable worker. PDFJS.disableWorker = true; Object.defineProperty(xhrPrototype, 'responseType', { get: function xmlHttpRequestGetResponseType() { return this._responseType || 'text'; }, set: function xmlHttpRequestSetResponseType(value) { if (value === 'text' || value === 'arraybuffer') { this._responseType = value; if (value === 'arraybuffer' && typeof this.overrideMimeType === 'function') { this.overrideMimeType('text/plain; charset=x-user-defined'); } } } }); // Support: IE9 if (typeof VBArray !== 'undefined') { Object.defineProperty(xhrPrototype, 'response', { get: function xmlHttpRequestResponseGet() { if (this.responseType === 'arraybuffer') { return new Uint8Array(new VBArray(this.responseBody).toArray()); } else { return this.responseText; } } }); return; } Object.defineProperty(xhrPrototype, 'response', { get: function xmlHttpRequestResponseGet() { if (this.responseType !== 'arraybuffer') { return this.responseText; } var text = this.responseText; var i, n = text.length; var result = new Uint8Array(n); for (i = 0; i < n; ++i) { result[i] = text.charCodeAt(i) & 0xFF; } return result.buffer; } }); })(); // window.btoa (base64 encode function) ? // Support: IE<10 (function checkWindowBtoaCompatibility() { if ('btoa' in window) { return; } var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; window.btoa = function windowBtoa(chars) { var buffer = ''; var i, n; for (i = 0, n = chars.length; i < n; i += 3) { var b1 = chars.charCodeAt(i) & 0xFF; var b2 = chars.charCodeAt(i + 1) & 0xFF; var b3 = chars.charCodeAt(i + 2) & 0xFF; var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4); var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64; var d4 = i + 2 < n ? (b3 & 0x3F) : 64; buffer += (digits.charAt(d1) + digits.charAt(d2) + digits.charAt(d3) + digits.charAt(d4)); } return buffer; }; })(); // window.atob (base64 encode function)? // Support: IE<10 (function checkWindowAtobCompatibility() { if ('atob' in window) { return; } // https://github.com/davidchambers/Base64.js var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; window.atob = function (input) { input = input.replace(/=+$/, ''); if (input.length % 4 === 1) { throw new Error('bad atob input'); } for ( // initialize result and counters var bc = 0, bs, buffer, idx = 0, output = ''; // get next character buffer = input.charAt(idx++); // character found in table? // initialize bit storage and add its ascii value ~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, // and if not first of each 4 characters, // convert the first 8 bits to one ascii character bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0 ) { // try to find character in table (0-63, not found => -1) buffer = digits.indexOf(buffer); } return output; }; })(); // Function.prototype.bind? // Support: Android<4.0, iOS<6.0 (function checkFunctionPrototypeBindCompatibility() { if (typeof Function.prototype.bind !== 'undefined') { return; } Function.prototype.bind = function functionPrototypeBind(obj) { var fn = this, headArgs = Array.prototype.slice.call(arguments, 1); var bound = function functionPrototypeBindBound() { var args = headArgs.concat(Array.prototype.slice.call(arguments)); return fn.apply(obj, args); }; return bound; }; })(); // HTMLElement dataset property // Support: IE<11, Safari<5.1, Android<4.0 (function checkDatasetProperty() { var div = document.createElement('div'); if ('dataset' in div) { return; // dataset property exists } Object.defineProperty(HTMLElement.prototype, 'dataset', { get: function() { if (this._dataset) { return this._dataset; } var dataset = {}; for (var j = 0, jj = this.attributes.length; j < jj; j++) { var attribute = this.attributes[j]; if (attribute.name.substring(0, 5) !== 'data-') { continue; } var key = attribute.name.substring(5).replace(/\-([a-z])/g, function(all, ch) { return ch.toUpperCase(); }); dataset[key] = attribute.value; } Object.defineProperty(this, '_dataset', { value: dataset, writable: false, enumerable: false }); return dataset; }, enumerable: true }); })(); // HTMLElement classList property // Support: IE<10, Android<4.0, iOS<5.0 (function checkClassListProperty() { var div = document.createElement('div'); if ('classList' in div) { return; // classList property exists } function changeList(element, itemName, add, remove) { var s = element.className || ''; var list = s.split(/\s+/g); if (list[0] === '') { list.shift(); } var index = list.indexOf(itemName); if (index < 0 && add) { list.push(itemName); } if (index >= 0 && remove) { list.splice(index, 1); } element.className = list.join(' '); return (index >= 0); } var classListPrototype = { add: function(name) { changeList(this.element, name, true, false); }, contains: function(name) { return changeList(this.element, name, false, false); }, remove: function(name) { changeList(this.element, name, false, true); }, toggle: function(name) { changeList(this.element, name, true, true); } }; Object.defineProperty(HTMLElement.prototype, 'classList', { get: function() { if (this._classList) { return this._classList; } var classList = Object.create(classListPrototype, { element: { value: this, writable: false, enumerable: true } }); Object.defineProperty(this, '_classList', { value: classList, writable: false, enumerable: false }); return classList; }, enumerable: true }); })(); // Check console compatibility // In older IE versions the console object is not available // unless console is open. // Support: IE<10 (function checkConsoleCompatibility() { if (!('console' in window)) { window.console = { log: function() {}, error: function() {}, warn: function() {} }; } else if (!('bind' in console.log)) { // native functions in IE9 might not have bind console.log = (function(fn) { return function(msg) { return fn(msg); }; })(console.log); console.error = (function(fn) { return function(msg) { return fn(msg); }; })(console.error); console.warn = (function(fn) { return function(msg) { return fn(msg); }; })(console.warn); } })(); // Check onclick compatibility in Opera // Support: Opera<15 (function checkOnClickCompatibility() { // workaround for reported Opera bug DSK-354448: // onclick fires on disabled buttons with opaque content function ignoreIfTargetDisabled(event) { if (isDisabled(event.target)) { event.stopPropagation(); } } function isDisabled(node) { return node.disabled || (node.parentNode && isDisabled(node.parentNode)); } if (navigator.userAgent.indexOf('Opera') !== -1) { // use browser detection since we cannot feature-check this bug document.addEventListener('click', ignoreIfTargetDisabled, true); } })(); // Checks if possible to use URL.createObjectURL() // Support: IE (function checkOnBlobSupport() { // sometimes IE loosing the data created with createObjectURL(), see #3977 if (navigator.userAgent.indexOf('Trident') >= 0) { PDFJS.disableCreateObjectURL = true; } })(); // Checks if navigator.language is supported (function checkNavigatorLanguage() { if ('language' in navigator) { return; } PDFJS.locale = navigator.userLanguage || 'en-US'; })(); (function checkRangeRequests() { // Safari has issues with cached range requests see: // https://github.com/mozilla/pdf.js/issues/3260 // Last tested with version 6.0.4. // Support: Safari 6.0+ var isSafari = Object.prototype.toString.call( window.HTMLElement).indexOf('Constructor') > 0; // Older versions of Android (pre 3.0) has issues with range requests, see: // https://github.com/mozilla/pdf.js/issues/3381. // Make sure that we only match webkit-based Android browsers, // since Firefox/Fennec works as expected. // Support: Android<3.0 var regex = /Android\s[0-2][^\d]/; var isOldAndroid = regex.test(navigator.userAgent); // Range requests are broken in Chrome 39 and 40, https://crbug.com/442318 var isChromeWithRangeBug = /Chrome\/(39|40)\./.test(navigator.userAgent); if (isSafari || isOldAndroid || isChromeWithRangeBug) { PDFJS.disableRange = true; PDFJS.disableStream = true; } })(); // Check if the browser supports manipulation of the history. // Support: IE<10, Android<4.2 (function checkHistoryManipulation() { // Android 2.x has so buggy pushState support that it was removed in // Android 3.0 and restored as late as in Android 4.2. // Support: Android 2.x if (!history.pushState || navigator.userAgent.indexOf('Android 2.') >= 0) { PDFJS.disableHistory = true; } })(); // Support: IE<11, Chrome<21, Android<4.4, Safari<6 (function checkSetPresenceInImageData() { // IE < 11 will use window.CanvasPixelArray which lacks set function. if (window.CanvasPixelArray) { if (typeof window.CanvasPixelArray.prototype.set !== 'function') { window.CanvasPixelArray.prototype.set = function(arr) { for (var i = 0, ii = this.length; i < ii; i++) { this[i] = arr[i]; } }; } } else { // Old Chrome and Android use an inaccessible CanvasPixelArray prototype. // Because we cannot feature detect it, we rely on user agent parsing. var polyfill = false, versionMatch; if (navigator.userAgent.indexOf('Chrom') >= 0) { versionMatch = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); // Chrome < 21 lacks the set function. polyfill = versionMatch && parseInt(versionMatch[2]) < 21; } else if (navigator.userAgent.indexOf('Android') >= 0) { // Android < 4.4 lacks the set function. // Android >= 4.4 will contain Chrome in the user agent, // thus pass the Chrome check above and not reach this block. polyfill = /Android\s[0-4][^\d]/g.test(navigator.userAgent); } else if (navigator.userAgent.indexOf('Safari') >= 0) { versionMatch = navigator.userAgent. match(/Version\/([0-9]+)\.([0-9]+)\.([0-9]+) Safari\//); // Safari < 6 lacks the set function. polyfill = versionMatch && parseInt(versionMatch[1]) < 6; } if (polyfill) { var contextPrototype = window.CanvasRenderingContext2D.prototype; var createImageData = contextPrototype.createImageData; contextPrototype.createImageData = function(w, h) { var imageData = createImageData.call(this, w, h); imageData.data.set = function(arr) { for (var i = 0, ii = this.length; i < ii; i++) { this[i] = arr[i]; } }; return imageData; }; // this closure will be kept referenced, so clear its vars contextPrototype = null; } } })(); // Support: IE<10, Android<4.0, iOS (function checkRequestAnimationFrame() { function fakeRequestAnimationFrame(callback) { window.setTimeout(callback, 20); } var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent); if (isIOS) { // requestAnimationFrame on iOS is broken, replacing with fake one. window.requestAnimationFrame = fakeRequestAnimationFrame; return; } if ('requestAnimationFrame' in window) { return; } window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || fakeRequestAnimationFrame; })(); (function checkCanvasSizeLimitation() { var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent); var isAndroid = /Android/g.test(navigator.userAgent); if (isIOS || isAndroid) { // 5MP PDFJS.maxCanvasPixels = 5242880; } })(); // Disable fullscreen support for certain problematic configurations. // Support: IE11+ (when embedded). (function checkFullscreenSupport() { var isEmbeddedIE = (navigator.userAgent.indexOf('Trident') >= 0 && window.parent !== window); if (isEmbeddedIE) { PDFJS.disableFullscreen = true; } })(); // Provides document.currentScript support // Support: IE, Chrome<29. (function checkCurrentScript() { if ('currentScript' in document) { return; } Object.defineProperty(document, 'currentScript', { get: function () { var scripts = document.getElementsByTagName('script'); return scripts[scripts.length - 1]; }, enumerable: true, configurable: true }); })(); /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global) {module.exports = global["pdfMake"] = __webpack_require__(1); /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(Buffer) {/* jslint node: true */ /* jslint browser: true */ /* global BlobBuilder */ 'use strict'; var PdfPrinter = __webpack_require__(2); var saveAs = __webpack_require__(3); var defaultClientFonts = { Roboto: { normal: 'Roboto-Regular.ttf', bold: 'Roboto-Medium.ttf', italics: 'Roboto-Italic.ttf', bolditalics: 'Roboto-Italic.ttf' } }; function Document(docDefinition, fonts, vfs) { this.docDefinition = docDefinition; this.fonts = fonts || defaultClientFonts; this.vfs = vfs; } Document.prototype._createDoc = function(options, callback) { var printer = new PdfPrinter(this.fonts); printer.fs.bindFS(this.vfs); var doc = printer.createPdfKitDocument(this.docDefinition, options); var chunks = []; var result; doc.on('data', function(chunk) { chunks.push(chunk); }); doc.on('end', function() { result = Buffer.concat(chunks); callback(result, doc._pdfMakePages); }); doc.end(); }; Document.prototype._getPages = function(options, cb){ if (!cb) throw 'getBuffer is an async method and needs a callback argument'; this._createDoc(options, function(ignoreBuffer, pages){ cb(pages); }); }; Document.prototype.open = function(message) { // we have to open the window immediately and store the reference // otherwise popup blockers will stop us var win = window.open('', '_blank'); try { this.getDataUrl(function(result) { win.location.href = result; }); } catch(e) { win.close(); throw e; } }; Document.prototype.print = function() { this.getDataUrl(function(dataUrl) { var iFrame = document.createElement('iframe'); iFrame.style.position = 'absolute'; iFrame.style.left = '-99999px'; iFrame.src = dataUrl; iFrame.onload = function() { function removeIFrame(){ document.body.removeChild(iFrame); document.removeEventListener('click', removeIFrame); } document.addEventListener('click', removeIFrame, false); }; document.body.appendChild(iFrame); }, { autoPrint: true }); }; Document.prototype.download = function(defaultFileName, cb) { if(typeof defaultFileName === "function") { cb = defaultFileName; defaultFileName = null; } defaultFileName = defaultFileName || 'file.pdf'; this.getBuffer(function(result) { saveAs(new Blob([result], {type: 'application/pdf'}), defaultFileName); if (typeof cb === "function") { cb(); } }); }; Document.prototype.getBase64 = function(cb, options) { if (!cb) throw 'getBase64 is an async method and needs a callback argument'; this._createDoc(options, function(buffer) { cb(buffer.toString('base64')); }); }; Document.prototype.getDataUrl = function(cb, options) { if (!cb) throw 'getDataUrl is an async method and needs a callback argument'; this._createDoc(options, function(buffer) { cb('data:application/pdf;base64,' + buffer.toString('base64')); }); }; Document.prototype.getBuffer = function(cb, options) { if (!cb) throw 'getBuffer is an async method and needs a callback argument'; this._createDoc(options, function(buffer){ cb(buffer); }); }; module.exports = { createPdf: function(docDefinition) { return new Document(docDefinition, window.pdfMake.fonts, window.pdfMake.vfs); } }; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4).Buffer)) /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { /* jslint node: true */ /* global window */ 'use strict'; var _ = __webpack_require__(11); var FontProvider = __webpack_require__(5); var LayoutBuilder = __webpack_require__(6); var PdfKit = __webpack_require__(28); var PDFReference = __webpack_require__(12); var sizes = __webpack_require__(7); var ImageMeasure = __webpack_require__(8); var textDecorator = __webpack_require__(9); var FontProvider = __webpack_require__(5); //////////////////////////////////////// // PdfPrinter /** * @class Creates an instance of a PdfPrinter which turns document definition into a pdf * * @param {Object} fontDescriptors font definition dictionary * * @example * var fontDescriptors = { * Roboto: { * normal: 'fonts/Roboto-Regular.ttf', * bold: 'fonts/Roboto-Medium.ttf', * italics: 'fonts/Roboto-Italic.ttf', * bolditalics: 'fonts/Roboto-Italic.ttf' * } * }; * * var printer = new PdfPrinter(fontDescriptors); */ function PdfPrinter(fontDescriptors) { this.fontDescriptors = fontDescriptors; } /** * Executes layout engine for the specified document and renders it into a pdfkit document * ready to be saved. * * @param {Object} docDefinition document definition * @param {Object} docDefinition.content an array describing the pdf structure (for more information take a look at the examples in the /examples folder) * @param {Object} [docDefinition.defaultStyle] default (implicit) style definition * @param {Object} [docDefinition.styles] dictionary defining all styles which can be used in the document * @param {Object} [docDefinition.pageSize] page size (pdfkit units, A4 dimensions by default) * @param {Number} docDefinition.pageSize.width width * @param {Number} docDefinition.pageSize.height height * @param {Object} [docDefinition.pageMargins] page margins (pdfkit units) * * @example * * var docDefinition = { * content: [ * 'First paragraph', * 'Second paragraph, this time a little bit longer', * { text: 'Third paragraph, slightly bigger font size', fontSize: 20 }, * { text: 'Another paragraph using a named style', style: 'header' }, * { text: ['playing with ', 'inlines' ] }, * { text: ['and ', { text: 'restyling ', bold: true }, 'them'] }, * ], * styles: { * header: { fontSize: 30, bold: true } * } * } * * var pdfDoc = printer.createPdfKitDocument(docDefinition); * * pdfDoc.pipe(fs.createWriteStream('sample.pdf')); * pdfDoc.end(); * * @return {Object} a pdfKit document object which can be saved or encode to data-url */ PdfPrinter.prototype.createPdfKitDocument = function(docDefinition, options) { options = options || {}; var pageSize = pageSize2widthAndHeight(docDefinition.pageSize || 'a4'); if(docDefinition.pageOrientation === 'landscape') { pageSize = { width: pageSize.height, height: pageSize.width}; } pageSize.orientation = docDefinition.pageOrientation === 'landscape' ? docDefinition.pageOrientation : 'portrait'; this.pdfKitDoc = new PdfKit({ size: [ pageSize.width, pageSize.height ], compress: false}); this.pdfKitDoc.info.Producer = 'pdfmake'; this.pdfKitDoc.info.Creator = 'pdfmake'; this.fontProvider = new FontProvider(this.fontDescriptors, this.pdfKitDoc); docDefinition.images = docDefinition.images || {}; var builder = new LayoutBuilder( pageSize, fixPageMargins(docDefinition.pageMargins || 40), new ImageMeasure(this.pdfKitDoc, docDefinition.images)); registerDefaultTableLayouts(builder); if (options.tableLayouts) { builder.registerTableLayouts(options.tableLayouts); } var pages = builder.layoutDocument(docDefinition.content, this.fontProvider, docDefinition.styles || {}, docDefinition.defaultStyle || { fontSize: 12, font: 'Roboto' }, docDefinition.background, docDefinition.header, docDefinition.footer, docDefinition.images, docDefinition.watermark, docDefinition.pageBreakBefore); renderPages(pages, this.fontProvider, this.pdfKitDoc); if(options.autoPrint){ var jsRef = this.pdfKitDoc.ref({ S: 'JavaScript', JS: new StringObject('this.print\\(true\\);') }); var namesRef = this.pdfKitDoc.ref({ Names: [new StringObject('EmbeddedJS'), new PDFReference(this.pdfKitDoc, jsRef.id)], }); jsRef.end(); namesRef.end(); this.pdfKitDoc._root.data.Names = { JavaScript: new PDFReference(this.pdfKitDoc, namesRef.id) }; } return this.pdfKitDoc; }; function fixPageMargins(margin) { if (!margin) return null; if (typeof margin === 'number' || margin instanceof Number) { margin = { left: margin, right: margin, top: margin, bottom: margin }; } else if (margin instanceof Array) { if (margin.length === 2) { margin = { left: margin[0], top: margin[1], right: margin[0], bottom: margin[1] }; } else if (margin.length === 4) { margin = { left: margin[0], top: margin[1], right: margin[2], bottom: margin[3] }; } else throw 'Invalid pageMargins definition'; } return margin; } function registerDefaultTableLayouts(layoutBuilder) { layoutBuilder.registerTableLayouts({ noBorders: { hLineWidth: function(i) { return 0; }, vLineWidth: function(i) { return 0; }, paddingLeft: function(i) { return i && 4 || 0; }, paddingRight: function(i, node) { return (i < node.table.widths.length - 1) ? 4 : 0; }, }, headerLineOnly: { hLineWidth: function(i, node) { if (i === 0 || i === node.table.body.length) return 0; return (i === node.table.headerRows) ? 2 : 0; }, vLineWidth: function(i) { return 0; }, paddingLeft: function(i) { return i === 0 ? 0 : 8; }, paddingRight: function(i, node) { return (i === node.table.widths.length - 1) ? 0 : 8; } }, lightHorizontalLines: { hLineWidth: function(i, node) { if (i === 0 || i === node.table.body.length) return 0; return (i === node.table.headerRows) ? 2 : 1; }, vLineWidth: function(i) { return 0; }, hLineColor: function(i) { return i === 1 ? 'black' : '#aaa'; }, paddingLeft: function(i) { return i === 0 ? 0 : 8; }, paddingRight: function(i, node) { return (i === node.table.widths.length - 1) ? 0 : 8; } } }); } var defaultLayout = { hLineWidth: function(i, node) { return 1; }, //return node.table.headerRows && i === node.table.headerRows && 3 || 0; }, vLineWidth: function(i, node) { return 1; }, hLineColor: function(i, node) { return 'black'; }, vLineColor: function(i, node) { return 'black'; }, paddingLeft: function(i, node) { return 4; }, //i && 4 || 0; }, paddingRight: function(i, node) { return 4; }, //(i < node.table.widths.length - 1) ? 4 : 0; }, paddingTop: function(i, node) { return 2; }, paddingBottom: function(i, node) { return 2; } }; function pageSize2widthAndHeight(pageSize) { if (typeof pageSize == 'string' || pageSize instanceof String) { var size = sizes[pageSize.toUpperCase()]; if (!size) throw ('Page size ' + pageSize + ' not recognized'); return { width: size[0], height: size[1] }; } return pageSize; } function StringObject(str){ this.isString = true; this.toString = function(){ return str; }; } function updatePageOrientationInOptions(currentPage, pdfKitDoc) { var previousPageOrientation = pdfKitDoc.options.size[0] > pdfKitDoc.options.size[1] ? 'landscape' : 'portrait'; if(currentPage.pageSize.orientation !== previousPageOrientation) { var width = pdfKitDoc.options.size[0]; var height = pdfKitDoc.options.size[1]; pdfKitDoc.options.size = [height, width]; } } function renderPages(pages, fontProvider, pdfKitDoc) { pdfKitDoc._pdfMakePages = pages; for (var i = 0; i < pages.length; i++) { if (i > 0) { updatePageOrientationInOptions(pages[i], pdfKitDoc); pdfKitDoc.addPage(pdfKitDoc.options); } var page = pages[i]; for(var ii = 0, il = page.items.length; ii < il; ii++) { var item = page.items[ii]; switch(item.type) { case 'vector': renderVector(item.item, pdfKitDoc); break; case 'line': renderLine(item.item, item.item.x, item.item.y, pdfKitDoc); break; case 'image': renderImage(item.item, item.item.x, item.item.y, pdfKitDoc); break; } } if(page.watermark){ renderWatermark(page, pdfKitDoc); } fontProvider.setFontRefsToPdfDoc(); } } function renderLine(line, x, y, pdfKitDoc) { x = x || 0; y = y || 0; var ascenderHeight = line.getAscenderHeight(); textDecorator.drawBackground(line, x, y, pdfKitDoc); //TODO: line.optimizeInlines(); for(var i = 0, l = line.inlines.length; i < l; i++) { var inline = line.inlines[i]; pdfKitDoc.fill(inline.color || 'black'); pdfKitDoc.save(); pdfKitDoc.transform(1, 0, 0, -1, 0, pdfKitDoc.page.height); var encoded = inline.font.encode(inline.text); pdfKitDoc.addContent('BT'); pdfKitDoc.addContent('' + (x + inline.x) + ' ' + (pdfKitDoc.page.height - y - ascenderHeight) + ' Td'); pdfKitDoc.addContent('/' + encoded.fontId + ' ' + inline.fontSize + ' Tf'); pdfKitDoc.addContent('<' + encoded.encodedText + '> Tj'); pdfKitDoc.addContent('ET'); pdfKitDoc.restore(); } textDecorator.drawDecorations(line, x, y, pdfKitDoc); } function renderWatermark(page, pdfKitDoc){ var watermark = page.watermark; pdfKitDoc.fill('black'); pdfKitDoc.opacity(0.1); pdfKitDoc.save(); pdfKitDoc.transform(1, 0, 0, -1, 0, pdfKitDoc.page.height); var angle = Math.atan2(pdfKitDoc.page.height, pdfKitDoc.page.width) * 180/Math.PI; pdfKitDoc.rotate(angle, {origin: [pdfKitDoc.page.width/2, pdfKitDoc.page.height/2]}); var encoded = watermark.font.encode(watermark.text); pdfKitDoc.addContent('BT'); pdfKitDoc.addContent('' + (pdfKitDoc.page.width/2 - watermark.size.size.width/2) + ' ' + (pdfKitDoc.page.height/2 - watermark.size.size.height/4) + ' Td'); // http://stackoverflow.com/a/32658671 //pdfKitDoc.addContent('/' + encoded.fontId + ' ' + watermark.size.fontSize + ' Tf'); //pdfKitDoc.addContent('<' + encoded.encodedText + '> Tj'); pdfKitDoc.addContent('/ ' + watermark.size.fontSize + ' Tf'); pdfKitDoc.addContent('(' + watermark.text + ') Tj'); pdfKitDoc.addContent('ET'); pdfKitDoc.restore(); } function renderVector(vector, pdfDoc) { //TODO: pdf optimization (there's no need to write all properties everytime) pdfDoc.lineWidth(vector.lineWidth || 1); if (vector.dash) { pdfDoc.dash(vector.dash.length, { space: vector.dash.space || vector.dash.length }); } else { pdfDoc.undash(); } pdfDoc.fillOpacity(vector.fillOpacity || 1); pdfDoc.strokeOpacity(vector.strokeOpacity || 1); pdfDoc.lineJoin(vector.lineJoin || 'miter'); //TODO: clipping switch(vector.type) { case 'ellipse': pdfDoc.ellipse(vector.x, vector.y, vector.r1, vector.r2); break; case 'rect': if (vector.r) { pdfDoc.roundedRect(vector.x, vector.y, vector.w, vector.h, vector.r); } else { pdfDoc.rect(vector.x, vector.y, vector.w, vector.h); } break; case 'line': pdfDoc.moveTo(vector.x1, vector.y1); pdfDoc.lineTo(vector.x2, vector.y2); break; case 'polyline': if (vector.points.length === 0) break; pdfDoc.moveTo(vector.points[0].x, vector.points[0].y); for(var i = 1, l = vector.points.length; i < l; i++) { pdfDoc.lineTo(vector.points[i].x, vector.points[i].y); } if (vector.points.length > 1) { var p1 = vector.points[0]; var pn = vector.points[vector.points.length - 1]; if (vector.closePath || p1.x === pn.x && p1.y === pn.y) { pdfDoc.closePath(); } } break; } if (vector.color && vector.lineColor) { pdfDoc.fillAndStroke(vector.color, vector.lineColor); } else if (vector.color) { pdfDoc.fill(vector.color); } else { pdfDoc.stroke(vector.lineColor || 'black'); } } function renderImage(image, x, y, pdfKitDoc) { pdfKitDoc.image(image.image, image.x, image.y, { width: image._width, height: image._height }); } module.exports = PdfPrinter; /* temporary browser extension */ PdfPrinter.prototype.fs = __webpack_require__(10); /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module) {/* FileSaver.js * A saveAs() FileSaver implementation. * 2014-08-29 * * By Eli Grey, http://eligrey.com * License: X11/MIT * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md */ /*global self */ /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ var saveAs = saveAs // IE 10+ (native saveAs) || (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) // Everyone else || (function(view) { "use strict"; // IE <10 is explicitly unsupported if (typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { return; } var doc = view.document // only get URL when necessary in case Blob.js hasn't overridden it yet , get_URL = function() { return view.URL || view.webkitURL || view; } , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") , can_use_save_link = "download" in save_link , click = function(node) { var event = doc.createEvent("MouseEvents"); event.initMouseEvent( "click", true, false, view, 0, 0, 0, 0, 0 , false, false, false, false, 0, null ); node.dispatchEvent(event); } , webkit_req_fs = view.webkitRequestFileSystem , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem , throw_outside = function(ex) { (view.setImmediate || view.setTimeout)(function() { throw ex; }, 0); } , force_saveable_type = "application/octet-stream" , fs_min_size = 0 // See https://code.google.com/p/chromium/issues/detail?id=375297#c7 for // the reasoning behind the timeout and revocation flow , arbitrary_revoke_timeout = 10 , revoke = function(file) { var revoker = function() { if (typeof file === "string") { // file is an object URL get_URL().revokeObjectURL(file); } else { // file is a File file.remove(); } }; if (view.chrome) { revoker(); } else { setTimeout(revoker, arbitrary_revoke_timeout); } } , dispatch = function(filesaver, event_types, event) { event_types = [].concat(event_types); var i = event_types.length; while (i--) { var listener = filesaver["on" + event_types[i]]; if (typeof listener === "function") { try { listener.call(filesaver, event || filesaver); } catch (ex) { throw_outside(ex); } } } } , FileSaver = function(blob, name) { // First try a.download, then web filesystem, then object URLs var filesaver = this , type = blob.type , blob_changed = false , object_url , target_view , dispatch_all = function() { dispatch(filesaver, "writestart progress write writeend".split(" ")); } // on any filesys errors revert to saving with object URLs , fs_error = function() { // don't create more object URLs than needed if (blob_changed || !object_url) { object_url = get_URL().createObjectURL(blob); } if (target_view) { target_view.location.href = object_url; } else { var new_tab = view.open(object_url, "_blank"); if (new_tab == undefined && typeof safari !== "undefined") { //Apple do not allow window.open, see http://bit.ly/1kZffRI view.location.href = object_url } } filesaver.readyState = filesaver.DONE; dispatch_all(); revoke(object_url); } , abortable = function(func) { return function() { if (filesaver.readyState !== filesaver.DONE) { return func.apply(this, arguments); } }; } , create_if_not_found = {create: true, exclusive: false} , slice ; filesaver.readyState = filesaver.INIT; if (!name) { name = "download"; } if (can_use_save_link) { object_url = get_URL().createObjectURL(blob); save_link.href = object_url; save_link.download = name; click(save_link); filesaver.readyState = filesaver.DONE; dispatch_all(); revoke(object_url); return; } // Object and web filesystem URLs have a problem saving in Google Chrome when // viewed in a tab, so I force save with application/octet-stream // http://code.google.com/p/chromium/issues/detail?id=91158 // Update: Google errantly closed 91158, I submitted it again: // https://code.google.com/p/chromium/issues/detail?id=389642 if (view.chrome && type && type !== force_saveable_type) { slice = blob.slice || blob.webkitSlice; blob = slice.call(blob, 0, blob.size, force_saveable_type); blob_changed = true; } // Since I can't be sure that the guessed media type will trigger a download // in WebKit, I append .download to the filename. // https://bugs.webkit.org/show_bug.cgi?id=65440 if (webkit_req_fs && name !== "download") { name += ".download"; } if (type === force_saveable_type || webkit_req_fs) { target_view = view; } if (!req_fs) { fs_error(); return; } fs_min_size += blob.size; req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { var save = function() { dir.getFile(name, create_if_not_found, abortable(function(file) { file.createWriter(abortable(function(writer) { writer.onwriteend = function(event) { target_view.location.href = file.toURL(); filesaver.readyState = filesaver.DONE; dispatch(filesaver, "writeend", event); revoke(file); }; writer.onerror = function() { var error = writer.error; if (error.code !== error.ABORT_ERR) { fs_error(); } }; "writestart progress write abort".split(" ").forEach(function(event) { writer["on" + event] = filesaver["on" + event]; }); writer.write(blob); filesaver.abort = function() { writer.abort(); filesaver.readyState = filesaver.DONE; }; filesaver.readyState = filesaver.WRITING; }), fs_error); }), fs_error); }; dir.getFile(name, {create: false}, abortable(function(file) { // delete file if it already exists file.remove(); save(); }), abortable(function(ex) { if (ex.code === ex.NOT_FOUND_ERR) { save(); } else { fs_error(); } })); }), fs_error); }), fs_error); } , FS_proto = FileSaver.prototype , saveAs = function(blob, name) { return new FileSaver(blob, name); } ; FS_proto.abort = function() { var filesaver = this; filesaver.readyState = filesaver.DONE; dispatch(filesaver, "abort"); }; FS_proto.readyState = FS_proto.INIT = 0; FS_proto.WRITING = 1; FS_proto.DONE = 2; FS_proto.error = FS_proto.onwritestart = FS_proto.onprogress = FS_proto.onwrite = FS_proto.onabort = FS_proto.onerror = FS_proto.onwriteend = null; return saveAs; }( typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content )); // `self` is undefined in Firefox for Android content script context // while `this` is nsIContentFrameMessageManager // with an attribute `content` that corresponds to the window if (typeof module !== "undefined" && module !== null) { module.exports = saveAs; } else if (("function" !== "undefined" && __webpack_require__(13) !== null) && (__webpack_require__(14) != null)) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function() { return saveAs; }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(15)(module))) /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(Buffer) {/*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT */ var base64 = __webpack_require__(31) var ieee754 = __webpack_require__(29) var isArray = __webpack_require__(30) exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 Buffer.poolSize = 8192 // not used by this implementation var kMaxLength = 0x3fffffff var rootParent = {} /** * If `Buffer.TYPED_ARRAY_SUPPORT`: * === true Use Uint8Array implementation (fastest) * === false Use Object implementation (most compatible, even IE6) * * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, * Opera 11.6+, iOS 4.2+. * * Note: * * - Implementation must support adding new properties to `Uint8Array` instances. * Firefox 4-29 lacked support, fixed in Firefox 30+. * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. * * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. * * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of * incorrect length in some situations. * * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will * get the Object implementation, which is slower but will work correctly. */ Buffer.TYPED_ARRAY_SUPPORT = (function () { try { var buf = new ArrayBuffer(0) var arr = new Uint8Array(buf) arr.foo = function () { return 42 } return arr.foo() === 42 && // typed array instances can be augmented typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` } catch (e) { return false } })() /** * Class: Buffer * ============= * * The Buffer constructor returns instances of `Uint8Array` that are augmented * with function properties for all the node `Buffer` API functions. We use * `Uint8Array` so that square bracket notation works as expected -- it returns * a single octet. * * By augmenting the instances, we can avoid modifying the `Uint8Array` * prototype. */ function Buffer (arg) { if (!(this instanceof Buffer)) { // Avoid going through an ArgumentsAdaptorTrampoline in the common case. if (arguments.length > 1) return new Buffer(arg, arguments[1]) return new Buffer(arg) } this.length = 0 this.parent = undefined // Common case. if (typeof arg === 'number') { return fromNumber(this, arg) } // Slightly less common case. if (typeof arg === 'string') { return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') } // Unusual. return fromObject(this, arg) } function fromNumber (that, length) { that = allocate(that, length < 0 ? 0 : checked(length) | 0) if (!Buffer.TYPED_ARRAY_SUPPORT) { for (var i = 0; i < length; i++) { that[i] = 0 } } return that } function fromString (that, string, encoding) { if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' // Assumption: byteLength() return value is always < kMaxLength. var length = byteLength(string, encoding) | 0 that = allocate(that, length) that.write(string, encoding) return that } function fromObject (that, object) { if (Buffer.isBuffer(object)) return fromBuffer(that, object) if (isArray(object)) return fromArray(that, object) if (object == null) { throw new TypeError('must start with number, buffer, array or string') } if (typeof ArrayBuffer !== 'undefined' && object.buffer instanceof ArrayBuffer) { return fromTypedArray(that, object) } if (object.length) return fromArrayLike(that, object) return fromJsonObject(that, object) } function fromBuffer (that, buffer) { var length = checked(buffer.length) | 0 that = allocate(that, length) buffer.copy(that, 0, 0, length) return that } function fromArray (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } // Duplicate of fromArray() to keep fromArray() monomorphic. function fromTypedArray (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) // Truncating the elements is probably not what people expect from typed // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior // of the old Buffer constructor. for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } function fromArrayLike (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } // Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. // Returns a zero-length buffer for inputs that don't conform to the spec. function fromJsonObject (that, object) { var array var length = 0 if (object.type === 'Buffer' && isArray(object.data)) { array = object.data length = checked(array.length) | 0 } that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } function allocate (that, length) { if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance that = Buffer._augment(new Uint8Array(length)) } else { // Fallback: Return an object instance of the Buffer class that.length = length that._isBuffer = true } var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 if (fromPool) that.parent = rootParent return that } function checked (length) { // Note: cannot use `length < kMaxLength` here because that fails when // length is NaN (which is otherwise coerced to zero.) if (length >= kMaxLength) { throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + kMaxLength.toString(16) + ' bytes') } return length | 0 } function SlowBuffer (subject, encoding) { if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) var buf = new Buffer(subject, encoding) delete buf.parent return buf } Buffer.isBuffer = function isBuffer (b) { return !!(b != null && b._isBuffer) } Buffer.compare = function compare (a, b) { if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { throw new TypeError('Arguments must be Buffers') } if (a === b) return 0 var x = a.length var y = b.length var i = 0 var len = Math.min(x, y) while (i < len) { if (a[i] !== b[i]) break ++i } if (i !== len) { x = a[i] y = b[i] } if (x < y) return -1 if (y < x) return 1 return 0 } Buffer.isEncoding = function isEncoding (encoding) { switch (String(encoding).toLowerCase()) { case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'raw': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return true default: return false } } Buffer.concat = function concat (list, length) { if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') if (list.length === 0) { return new Buffer(0) } else if (list.length === 1) { return list[0] } var i if (length === undefined) { length = 0 for (i = 0; i < list.length; i++) { length += list[i].length } } var buf = new Buffer(length) var pos = 0 for (i = 0; i < list.length; i++) { var item = list[i] item.copy(buf, pos) pos += item.length } return buf } function byteLength (string, encoding) { if (typeof string !== 'string') string = String(string) if (string.length === 0) return 0 switch (encoding || 'utf8') { case 'ascii': case 'binary': case 'raw': return string.length case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return string.length * 2 case 'hex': return string.length >>> 1 case 'utf8': case 'utf-8': return utf8ToBytes(string).length case 'base64': return base64ToBytes(string).length default: return string.length } } Buffer.byteLength = byteLength // pre-set for values that may exist in the future Buffer.prototype.length = undefined Buffer.prototype.parent = undefined // toString(encoding, start=0, end=buffer.length) Buffer.prototype.toString = function toString (encoding, start, end) { var loweredCase = false start = start | 0 end = end === undefined || end === Infinity ? this.length : end | 0 if (!encoding) encoding = 'utf8' if (start < 0) start = 0 if (end > this.length) end = this.length if (end <= start) return '' while (true) { switch (encoding) { case 'hex': return hexSlice(this, start, end) case 'utf8': case 'utf-8': return utf8Slice(this, start, end) case 'ascii': return asciiSlice(this, start, end) case 'binary': return binarySlice(this, start, end) case 'base64': return base64Slice(this, start, end) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return utf16leSlice(this, start, end) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = (encoding + '').toLowerCase() loweredCase = true } } } Buffer.prototype.equals = function equals (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') if (this === b) return true return Buffer.compare(this, b) === 0 } Buffer.prototype.inspect = function inspect () { var str = '' var max = exports.INSPECT_MAX_BYTES if (this.length > 0) { str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') if (this.length > max) str += ' ... ' } return '' } Buffer.prototype.compare = function compare (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') if (this === b) return 0 return Buffer.compare(this, b) } Buffer.prototype.indexOf = function indexOf (val, byteOffset) { if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff else if (byteOffset < -0x80000000) byteOffset = -0x80000000 byteOffset >>= 0 if (this.length === 0) return -1 if (byteOffset >= this.length) return -1 // Negative offsets start from the end of the buffer if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) if (typeof val === 'string') { if (val.length === 0) return -1 // special case: looking for empty string always fails return String.prototype.indexOf.call(this, val, byteOffset) } if (Buffer.isBuffer(val)) { return arrayIndexOf(this, val, byteOffset) } if (typeof val === 'number') { if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { return Uint8Array.prototype.indexOf.call(this, val, byteOffset) } return arrayIndexOf(this, [ val ], byteOffset) } function arrayIndexOf (arr, val, byteOffset) { var foundIndex = -1 for (var i = 0; byteOffset + i < arr.length; i++) { if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { if (foundIndex === -1) foundIndex = i if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex } else { foundIndex = -1 } } return -1 } throw new TypeError('val must be string, number or Buffer') } // `get` will be removed in Node 0.13+ Buffer.prototype.get = function get (offset) { console.log('.get() is deprecated. Access using array indexes instead.') return this.readUInt8(offset) } // `set` will be removed in Node 0.13+ Buffer.prototype.set = function set (v, offset) { console.log('.set() is deprecated. Access using array indexes instead.') return this.writeUInt8(v, offset) } function hexWrite (buf, string, offset, length) { offset = Number(offset) || 0 var remaining = buf.length - offset if (!length) { length = remaining } else { length = Number(length) if (length > remaining) { length = remaining } } // must be an even number of digits var strLen = string.length if (strLen % 2 !== 0) throw new Error('Invalid hex string') if (length > strLen / 2) { length = strLen / 2 } for (var i = 0; i < length; i++) { var parsed = parseInt(string.substr(i * 2, 2), 16) if (isNaN(parsed)) throw new Error('Invalid hex string') buf[offset + i] = parsed } return i } function utf8Write (buf, string, offset, length) { return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) } function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } function binaryWrite (buf, string, offset, length) { return asciiWrite(buf, string, offset, length) } function base64Write (buf, string, offset, length) { return blitBuffer(base64ToBytes(string), buf, offset, length) } function ucs2Write (buf, string, offset, length) { return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) } Buffer.prototype.write = function write (string, offset, length, encoding) { // Buffer#write(string) if (offset === undefined) { encoding = 'utf8' length = this.length offset = 0 // Buffer#write(string, encoding) } else if (length === undefined && typeof offset === 'string') { encoding = offset length = this.length offset = 0 // Buffer#write(string, offset[, length][, encoding]) } else if (isFinite(offset)) { offset = offset | 0 if (isFinite(length)) { length = length | 0 if (encoding === undefined) encoding = 'utf8' } else { encoding = length length = undefined } // legacy write(string, encoding, offset, length) - remove in v0.13 } else { var swap = encoding encoding = offset offset = length | 0 length = swap } var remaining = this.length - offset if (length === undefined || length > remaining) length = remaining if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { throw new RangeError('attempt to write outside buffer bounds') } if (!encoding) encoding = 'utf8' var loweredCase = false for (;;) { switch (encoding) { case 'hex': return hexWrite(this, string, offset, length) case 'utf8': case 'utf-8': return utf8Write(this, string, offset, length) case 'ascii': return asciiWrite(this, string, offset, length) case 'binary': return binaryWrite(this, string, offset, length) case 'base64': // Warning: maxLength not taken into account in base64Write return base64Write(this, string, offset, length) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return ucs2Write(this, string, offset, length) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.prototype.toJSON = function toJSON () { return { type: 'Buffer', data: Array.prototype.slice.call(this._arr || this, 0) } } function base64Slice (buf, start, end) { if (start === 0 && end === buf.length) { return base64.fromByteArray(buf) } else { return base64.fromByteArray(buf.slice(start, end)) } } function utf8Slice (buf, start, end) { var res = '' var tmp = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { if (buf[i] <= 0x7F) { res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) tmp = '' } else { tmp += '%' + buf[i].toString(16) } } return res + decodeUtf8Char(tmp) } function asciiSlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i] & 0x7F) } return ret } function binarySlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i]) } return ret } function hexSlice (buf, start, end) { var len = buf.length if (!start || start < 0) start = 0 if (!end || end < 0 || end > len) end = len var out = '' for (var i = start; i < end; i++) { out += toHex(buf[i]) } return out } function utf16leSlice (buf, start, end) { var bytes = buf.slice(start, end) var res = '' for (var i = 0; i < bytes.length; i += 2) { res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) } return res } Buffer.prototype.slice = function slice (start, end) { var len = this.length start = ~~start end = end === undefined ? len : ~~end if (start < 0) { start += len if (start < 0) start = 0 } else if (start > len) { start = len } if (end < 0) { end += len if (end < 0) end = 0 } else if (end > len) { end = len } if (end < start) end = start var newBuf if (Buffer.TYPED_ARRAY_SUPPORT) { newBuf = Buffer._augment(this.subarray(start, end)) } else { var sliceLen = end - start newBuf = new Buffer(sliceLen, undefined) for (var i = 0; i < sliceLen; i++) { newBuf[i] = this[i + start] } } if (newBuf.length) newBuf.parent = this.parent || this return newBuf } /* * Need to make sure that buffer isn't trying to write out of bounds. */ function checkOffset (offset, ext, length) { if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') } Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } return val } Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) { checkOffset(offset, byteLength, this.length) } var val = this[offset + --byteLength] var mul = 1 while (byteLength > 0 && (mul *= 0x100)) { val += this[offset + --byteLength] * mul } return val } Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) return this[offset] } Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return this[offset] | (this[offset + 1] << 8) } Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return (this[offset] << 8) | this[offset + 1] } Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ((this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16)) + (this[offset + 3] * 0x1000000) } Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] * 0x1000000) + ((this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3]) } Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var i = byteLength var mul = 1 var val = this[offset + --i] while (i > 0 && (mul *= 0x100)) { val += this[offset + --i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) if (!(this[offset] & 0x80)) return (this[offset]) return ((0xff - this[offset] + 1) * -1) } Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset] | (this[offset + 1] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset + 1] | (this[offset] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16) | (this[offset + 3] << 24) } Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] << 24) | (this[offset + 1] << 16) | (this[offset + 2] << 8) | (this[offset + 3]) } Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, true, 23, 4) } Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, false, 23, 4) } Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, true, 52, 8) } Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, false, 52, 8) } function checkInt (buf, value, offset, ext, max, min) { if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') if (value > max || value < min) throw new RangeError('value is out of bounds') if (offset + ext > buf.length) throw new RangeError('index out of range') } Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) var mul = 1 var i = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) var i = byteLength - 1 var mul = 1 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) this[offset] = value return offset + 1 } function objectWriteUInt16 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> (littleEndian ? i : 1 - i) * 8 } } Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = value this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = value } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } function objectWriteUInt32 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffffffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff } } Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset + 3] = (value >>> 24) this[offset + 2] = (value >>> 16) this[offset + 1] = (value >>> 8) this[offset] = value } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = value } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = 0 var mul = 1 var sub = value < 0 ? 1 : 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = byteLength - 1 var mul = 1 var sub = value < 0 ? 1 : 0 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) if (value < 0) value = 0xff + value + 1 this[offset] = value return offset + 1 } Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = value this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = value } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = value this[offset + 1] = (value >>> 8) this[offset + 2] = (value >>> 16) this[offset + 3] = (value >>> 24) } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (value < 0) value = 0xffffffff + value + 1 if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = value } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } function checkIEEE754 (buf, value, offset, ext, max, min) { if (value > max || value < min) throw new RangeError('value is out of bounds') if (offset + ext > buf.length) throw new RangeError('index out of range') if (offset < 0) throw new RangeError('index out of range') } function writeFloat (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) } ieee754.write(buf, value, offset, littleEndian, 23, 4) return offset + 4 } Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { return writeFloat(this, value, offset, true, noAssert) } Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { return writeFloat(this, value, offset, false, noAssert) } function writeDouble (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) } ieee754.write(buf, value, offset, littleEndian, 52, 8) return offset + 8 } Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { return writeDouble(this, value, offset, true, noAssert) } Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { return writeDouble(this, value, offset, false, noAssert) } // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) Buffer.prototype.copy = function copy (target, targetStart, start, end) { if (!start) start = 0 if (!end && end !== 0) end = this.length if (targetStart >= target.length) targetStart = target.length if (!targetStart) targetStart = 0 if (end > 0 && end < start) end = start // Copy 0 bytes; we're done if (end === start) return 0 if (target.length === 0 || this.length === 0) return 0 // Fatal error conditions if (targetStart < 0) { throw new RangeError('targetStart out of bounds') } if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') if (end < 0) throw new RangeError('sourceEnd out of bounds') // Are we oob? if (end > this.length) end = this.length if (target.length - targetStart < end - start) { end = target.length - targetStart + start } var len = end - start if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { for (var i = 0; i < len; i++) { target[i + targetStart] = this[i + start] } } else { target._set(this.subarray(start, start + len), targetStart) } return len } // fill(value, start=0, end=buffer.length) Buffer.prototype.fill = function fill (value, start, end) { if (!value) value = 0 if (!start) start = 0 if (!end) end = this.length if (end < start) throw new RangeError('end < start') // Fill 0 bytes; we're done if (end === start) return if (this.length === 0) return if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') if (end < 0 || end > this.length) throw new RangeError('end out of bounds') var i if (typeof value === 'number') { for (i = start; i < end; i++) { this[i] = value } } else { var bytes = utf8ToBytes(value.toString()) var len = bytes.length for (i = start; i < end; i++) { this[i] = bytes[i % len] } } return this } /** * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. * Added in Node 0.12. Only available in browsers that support ArrayBuffer. */ Buffer.prototype.toArrayBuffer = function toArrayBuffer () { if (typeof Uint8Array !== 'undefined') { if (Buffer.TYPED_ARRAY_SUPPORT) { return (new Buffer(this)).buffer } else { var buf = new Uint8Array(this.length) for (var i = 0, len = buf.length; i < len; i += 1) { buf[i] = this[i] } return buf.buffer } } else { throw new TypeError('Buffer.toArrayBuffer not supported in this browser') } } // HELPER FUNCTIONS // ================ var BP = Buffer.prototype /** * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods */ Buffer._augment = function _augment (arr) { arr.constructor = Buffer arr._isBuffer = true // save reference to original Uint8Array set method before overwriting arr._set = arr.set // deprecated, will be removed in node 0.13+ arr.get = BP.get arr.set = BP.set arr.write = BP.write arr.toString = BP.toString arr.toLocaleString = BP.toString arr.toJSON = BP.toJSON arr.equals = BP.equals arr.compare = BP.compare arr.indexOf = BP.indexOf arr.copy = BP.copy arr.slice = BP.slice arr.readUIntLE = BP.readUIntLE arr.readUIntBE = BP.readUIntBE arr.readUInt8 = BP.readUInt8 arr.readUInt16LE = BP.readUInt16LE arr.readUInt16BE = BP.readUInt16BE arr.readUInt32LE = BP.readUInt32LE arr.readUInt32BE = BP.readUInt32BE arr.readIntLE = BP.readIntLE arr.readIntBE = BP.readIntBE arr.readInt8 = BP.readInt8 arr.readInt16LE = BP.readInt16LE arr.readInt16BE = BP.readInt16BE arr.readInt32LE = BP.readInt32LE arr.readInt32BE = BP.readInt32BE arr.readFloatLE = BP.readFloatLE arr.readFloatBE = BP.readFloatBE arr.readDoubleLE = BP.readDoubleLE arr.readDoubleBE = BP.readDoubleBE arr.writeUInt8 = BP.writeUInt8 arr.writeUIntLE = BP.writeUIntLE arr.writeUIntBE = BP.writeUIntBE arr.writeUInt16LE = BP.writeUInt16LE arr.writeUInt16BE = BP.writeUInt16BE arr.writeUInt32LE = BP.writeUInt32LE arr.writeUInt32BE = BP.writeUInt32BE arr.writeIntLE = BP.writeIntLE arr.writeIntBE = BP.writeIntBE arr.writeInt8 = BP.writeInt8 arr.writeInt16LE = BP.writeInt16LE arr.writeInt16BE = BP.writeInt16BE arr.writeInt32LE = BP.writeInt32LE arr.writeInt32BE = BP.writeInt32BE arr.writeFloatLE = BP.writeFloatLE arr.writeFloatBE = BP.writeFloatBE arr.writeDoubleLE = BP.writeDoubleLE arr.writeDoubleBE = BP.writeDoubleBE arr.fill = BP.fill arr.inspect = BP.inspect arr.toArrayBuffer = BP.toArrayBuffer return arr } var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g function base64clean (str) { // Node strips out invalid characters like \n and \t from the string, base64-js does not str = stringtrim(str).replace(INVALID_BASE64_RE, '') // Node converts strings with length < 2 to '' if (str.length < 2) return '' // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not while (str.length % 4 !== 0) { str = str + '=' } return str } function stringtrim (str) { if (str.trim) return str.trim() return str.replace(/^\s+|\s+$/g, '') } function toHex (n) { if (n < 16) return '0' + n.toString(16) return n.toString(16) } function utf8ToBytes (string, units) { units = units || Infinity var codePoint var length = string.length var leadSurrogate = null var bytes = [] var i = 0 for (; i < length; i++) { codePoint = string.charCodeAt(i) // is surrogate component if (codePoint > 0xD7FF && codePoint < 0xE000) { // last char was a lead if (leadSurrogate) { // 2 leads in a row if (codePoint < 0xDC00) { if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) leadSurrogate = codePoint continue } else { // valid surrogate pair codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 leadSurrogate = null } } else { // no lead yet if (codePoint > 0xDBFF) { // unexpected trail if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } else if (i + 1 === length) { // unpaired lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } else { // valid lead leadSurrogate = codePoint continue } } } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) leadSurrogate = null } // encode utf8 if (codePoint < 0x80) { if ((units -= 1) < 0) break bytes.push(codePoint) } else if (codePoint < 0x800) { if ((units -= 2) < 0) break bytes.push( codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x10000) { if ((units -= 3) < 0) break bytes.push( codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x200000) { if ((units -= 4) < 0) break bytes.push( codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else { throw new Error('Invalid code point') } } return bytes } function asciiToBytes (str) { var byteArray = [] for (var i = 0; i < str.length; i++) { // Node's code seems to be doing this and not & 0x7F.. byteArray.push(str.charCodeAt(i) & 0xFF) } return byteArray } function utf16leToBytes (str, units) { var c, hi, lo var byteArray = [] for (var i = 0; i < str.length; i++) { if ((units -= 2) < 0) break c = str.charCodeAt(i) hi = c >> 8 lo = c % 256 byteArray.push(lo) byteArray.push(hi) } return byteArray } function base64ToBytes (str) { return base64.toByteArray(base64clean(str)) } function blitBuffer (src, dst, offset, length) { for (var i = 0; i < length; i++) { if ((i + offset >= dst.length) || (i >= src.length)) break dst[i + offset] = src[i] } return i } function decodeUtf8Char (str) { try { return decodeURIComponent(str) } catch (err) { return String.fromCharCode(0xFFFD) // UTF 8 invalid char } } /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4).Buffer)) /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { /* jslint node: true */ 'use strict'; var _ = __webpack_require__(11); var FontWrapper = __webpack_require__(16); function typeName(bold, italics){ var type = 'normal'; if (bold && italics) type = 'bolditalics'; else if (bold) type = 'bold'; else if (italics) type = 'italics'; return type; } function FontProvider(fontDescriptors, pdfDoc) { this.fonts = {}; this.pdfDoc = pdfDoc; this.fontWrappers = {}; for(var font in fontDescriptors) { if (fontDescriptors.hasOwnProperty(font)) { var fontDef = fontDescriptors[font]; this.fonts[font] = { normal: fontDef.normal, bold: fontDef.bold, italics: fontDef.italics, bolditalics: fontDef.bolditalics }; } } } FontProvider.prototype.provideFont = function(familyName, bold, italics) { if (!this.fonts[familyName]) return this.pdfDoc._font; var type = typeName(bold, italics); this.fontWrappers[familyName] = this.fontWrappers[familyName] || {}; if (!this.fontWrappers[familyName][type]) { this.fontWrappers[familyName][type] = new FontWrapper(this.pdfDoc, this.fonts[familyName][type], familyName + '(' + type + ')'); } return this.fontWrappers[familyName][type]; }; FontProvider.prototype.setFontRefsToPdfDoc = function(){ var self = this; _.each(self.fontWrappers, function(fontFamily) { _.each(fontFamily, function(fontWrapper){ _.each(fontWrapper.pdfFonts, function(font){ if (!self.pdfDoc.page.fonts[font.id]) { self.pdfDoc.page.fonts[font.id] = font.ref(); } }); }); }); }; module.exports = FontProvider; /***/ }, /* 6 */ /***/ function(module, exports, __webpack_require__) { /* jslint node: true */ 'use strict'; var _ = __webpack_require__(11); var TraversalTracker = __webpack_require__(18); var DocMeasure = __webpack_require__(19); var DocumentContext = __webpack_require__(20); var PageElementWriter = __webpack_require__(21); var ColumnCalculator = __webpack_require__(22); var TableProcessor = __webpack_require__(23); var Line = __webpack_require__(24); var pack = __webpack_require__(25).pack; var offsetVector = __webpack_require__(25).offsetVector; var fontStringify = __webpack_require__(25).fontStringify; var isFunction = __webpack_require__(25).isFunction; var TextTools = __webpack_require__(26); var StyleContextStack = __webpack_require__(27); function addAll(target, otherArray){ _.each(otherArray, function(item){ target.push(item); }); } /** * Creates an instance of LayoutBuilder - layout engine which turns document-definition-object * into a set of pages, lines, inlines and vectors ready to be rendered into a PDF * * @param {Object} pageSize - an object defining page width and height * @param {Object} pageMargins - an object defining top, left, right and bottom margins */ function LayoutBuilder(pageSize, pageMargins, imageMeasure) { this.pageSize = pageSize; this.pageMargins = pageMargins; this.tracker = new TraversalTracker(); this.imageMeasure = imageMeasure; this.tableLayouts = {}; } LayoutBuilder.prototype.registerTableLayouts = function (tableLayouts) { this.tableLayouts = pack(this.tableLayouts, tableLayouts); }; /** * Executes layout engine on document-definition-object and creates an array of pages * containing positioned Blocks, Lines and inlines * * @param {Object} docStructure document-definition-object * @param {Object} fontProvider font provider * @param {Object} styleDictionary dictionary with style definitions * @param {Object} defaultStyle default style definition * @return {Array} an array of pages */ LayoutBuilder.prototype.layoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct) { function addPageBreaksIfNecessary(linearNodeList, pages) { linearNodeList = _.reject(linearNodeList, function(node){ return _.isEmpty(node.positions); }); _.each(linearNodeList, function(node) { var nodeInfo = _.pick(node, [ 'id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'columns', 'headlineLevel', 'style', 'pageBreak', 'pageOrientation', 'width', 'height' ]); nodeInfo.startPosition = _.first(node.positions); nodeInfo.pageNumbers = _.chain(node.positions).map('pageNumber').uniq().value(); nodeInfo.pages = pages.length; nodeInfo.stack = _.isArray(node.stack); node.nodeInfo = nodeInfo; }); return _.any(linearNodeList, function (node, index, followingNodeList) { if (node.pageBreak !== 'before' && !node.pageBreakCalculated) { node.pageBreakCalculated = true; var pageNumber = _.first(node.nodeInfo.pageNumbers); var followingNodesOnPage = _.chain(followingNodeList).drop(index + 1).filter(function (node0) { return _.contains(node0.nodeInfo.pageNumbers, pageNumber); }).value(); var nodesOnNextPage = _.chain(followingNodeList).drop(index + 1).filter(function (node0) { return _.contains(node0.nodeInfo.pageNumbers, pageNumber + 1); }).value(); var previousNodesOnPage = _.chain(followingNodeList).take(index).filter(function (node0) { return _.contains(node0.nodeInfo.pageNumbers, pageNumber); }).value(); if (pageBreakBeforeFct(node.nodeInfo, _.map(followingNodesOnPage, 'nodeInfo'), _.map(nodesOnNextPage, 'nodeInfo'), _.map(previousNodesOnPage, 'nodeInfo'))) { node.pageBreak = 'before'; return true; } } }); } if(!isFunction(pageBreakBeforeFct)){ pageBreakBeforeFct = function(){ return false; }; } this.docMeasure = new DocMeasure(fontProvider, styleDictionary, defaultStyle, this.imageMeasure, this.tableLayouts, images); function resetXYs(result) { _.each(result.linearNodeList, function (node) { node.resetXY(); }); } var result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark); while(addPageBreaksIfNecessary(result.linearNodeList, result.pages)){ resetXYs(result); result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark); } return result.pages; }; LayoutBuilder.prototype.tryLayoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct) { this.linearNodeList = []; docStructure = this.docMeasure.measureDocument(docStructure); this.writer = new PageElementWriter( new DocumentContext(this.pageSize, this.pageMargins), this.tracker); var _this = this; this.writer.context().tracker.startTracking('pageAdded', function() { _this.addBackground(background); }); this.addBackground(background); this.processNode(docStructure); this.addHeadersAndFooters(header, footer); /* jshint eqnull:true */ if(watermark != null) this.addWatermark(watermark, fontProvider); return {pages: this.writer.context().pages, linearNodeList: this.linearNodeList}; }; LayoutBuilder.prototype.addBackground = function(background) { var backgroundGetter = isFunction(background) ? background : function() { return background; }; var pageBackground = backgroundGetter(this.writer.context().page + 1); if (pageBackground) { var pageSize = this.writer.context().getCurrentPage().pageSize; this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height); this.processNode(this.docMeasure.measureDocument(pageBackground)); this.writer.commitUnbreakableBlock(0, 0); } }; LayoutBuilder.prototype.addStaticRepeatable = function(headerOrFooter, sizeFunction) { this.addDynamicRepeatable(function() { return headerOrFooter; }, sizeFunction); }; LayoutBuilder.prototype.addDynamicRepeatable = function(nodeGetter, sizeFunction) { var pages = this.writer.context().pages; for(var pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) { this.writer.context().page = pageIndex; var node = nodeGetter(pageIndex + 1, l); if (node) { var sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins); this.writer.beginUnbreakableBlock(sizes.width, sizes.height); this.processNode(this.docMeasure.measureDocument(node)); this.writer.commitUnbreakableBlock(sizes.x, sizes.y); } } }; LayoutBuilder.prototype.addHeadersAndFooters = function(header, footer) { var headerSizeFct = function(pageSize, pageMargins){ return { x: 0, y: 0, width: pageSize.width, height: pageMargins.top }; }; var footerSizeFct = function (pageSize, pageMargins) { return { x: 0, y: pageSize.height - pageMargins.bottom, width: pageSize.width, height: pageMargins.bottom }; }; if(isFunction(header)) { this.addDynamicRepeatable(header, headerSizeFct); } else if(header) { this.addStaticRepeatable(header, headerSizeFct); } if(isFunction(footer)) { this.addDynamicRepeatable(footer, footerSizeFct); } else if(footer) { this.addStaticRepeatable(footer, footerSizeFct); } }; LayoutBuilder.prototype.addWatermark = function(watermark, fontProvider){ var defaultFont = Object.getOwnPropertyNames(fontProvider.fonts)[0]; // TODO allow selection of other font var watermarkObject = { text: watermark, font: fontProvider.provideFont(fontProvider[defaultFont], false, false), size: getSize(this.pageSize, watermark, fontProvider) }; var pages = this.writer.context().pages; for(var i = 0, l = pages.length; i < l; i++) { pages[i].watermark = watermarkObject; } function getSize(pageSize, watermark, fontProvider){ var width = pageSize.width; var height = pageSize.height; var targetWidth = Math.sqrt(width*width + height*height)*0.8; /* page diagnoal * sample factor */ var textTools = new TextTools(fontProvider); var styleContextStack = new StyleContextStack(); var size; /** * Binary search the best font size. * Initial bounds [0, 1000] * Break when range < 1 */ var a = 0; var b = 1000; var c = (a+b)/2; while(Math.abs(a - b) > 1){ styleContextStack.push({ fontSize: c }); size = textTools.sizeOfString(watermark, styleContextStack); if(size.width > targetWidth){ b = c; c = (a+b)/2; } else if(size.width < targetWidth){ a = c; c = (a+b)/2; } styleContextStack.pop(); } /* End binary search */ return {size: size, fontSize: c}; } }; function decorateNode(node){ var x = node.x, y = node.y; node.positions = []; _.each(node.canvas, function(vector){ var x = vector.x, y = vector.y; vector.resetXY = function(){ vector.x = x; vector.y = y; }; }); node.resetXY = function(){ node.x = x; node.y = y; _.each(node.canvas, function(vector){ vector.resetXY(); }); }; } LayoutBuilder.prototype.processNode = function(node) { var self = this; this.linearNodeList.push(node); decorateNode(node); applyMargins(function() { var absPosition = node.absolutePosition; if(absPosition){ self.writer.context().beginDetachedBlock(); self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0); } if (node.stack) { self.processVerticalContainer(node); } else if (node.columns) { self.processColumns(node); } else if (node.ul) { self.processList(false, node); } else if (node.ol) { self.processList(true, node); } else if (node.table) { self.processTable(node); } else if (node.text !== undefined) { self.processLeaf(node); } else if (node.image) { self.processImage(node); } else if (node.canvas) { self.processCanvas(node); } else if (node.qr) { self.processQr(node); }else if (!node._span) { throw 'Unrecognized document structure: ' + JSON.stringify(node, fontStringify); } if(absPosition){ self.writer.context().endDetachedBlock(); } }); function applyMargins(callback) { var margin = node._margin; if (node.pageBreak === 'before') { self.writer.moveToNextPage(node.pageOrientation); } if (margin) { self.writer.context().moveDown(margin[1]); self.writer.context().addMargin(margin[0], margin[2]); } callback(); if(margin) { self.writer.context().addMargin(-margin[0], -margin[2]); self.writer.context().moveDown(margin[3]); } if (node.pageBreak === 'after') { self.writer.moveToNextPage(node.pageOrientation); } } }; // vertical container LayoutBuilder.prototype.processVerticalContainer = function(node) { var self = this; node.stack.forEach(function(item) { self.processNode(item); addAll(node.positions, item.positions); //TODO: paragraph gap }); }; // columns LayoutBuilder.prototype.processColumns = function(columnNode) { var columns = columnNode.columns; var availableWidth = this.writer.context().availableWidth; var gaps = gapArray(columnNode._gap); if (gaps) availableWidth -= (gaps.length - 1) * columnNode._gap; ColumnCalculator.buildColumnWidths(columns, availableWidth); var result = this.processRow(columns, columns, gaps); addAll(columnNode.positions, result.positions); function gapArray(gap) { if (!gap) return null; var gaps = []; gaps.push(0); for(var i = columns.length - 1; i > 0; i--) { gaps.push(gap); } return gaps; } }; LayoutBuilder.prototype.processRow = function(columns, widths, gaps, tableBody, tableRow) { var self = this; var pageBreaks = [], positions = []; this.tracker.auto('pageChanged', storePageBreakData, function() { widths = widths || columns; self.writer.context().beginColumnGroup(); for(var i = 0, l = columns.length; i < l; i++) { var column = columns[i]; var width = widths[i]._calcWidth; var leftOffset = colLeftOffset(i); if (column.colSpan && column.colSpan > 1) { for(var j = 1; j < column.colSpan; j++) { width += widths[++i]._calcWidth + gaps[i]; } } self.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i)); if (!column._span) { self.processNode(column); addAll(positions, column.positions); } else if (column._columnEndingContext) { // row-span ending self.writer.context().markEnding(column); } } self.writer.context().completeColumnGroup(); }); return {pageBreaks: pageBreaks, positions: positions}; function storePageBreakData(data) { var pageDesc; for(var i = 0, l = pageBreaks.length; i < l; i++) { var desc = pageBreaks[i]; if (desc.prevPage === data.prevPage) { pageDesc = desc; break; } } if (!pageDesc) { pageDesc = data; pageBreaks.push(pageDesc); } pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY); pageDesc.y = Math.min(pageDesc.y, data.y); } function colLeftOffset(i) { if (gaps && gaps.length > i) return gaps[i]; return 0; } function getEndingCell(column, columnIndex) { if (column.rowSpan && column.rowSpan > 1) { var endingRow = tableRow + column.rowSpan - 1; if (endingRow >= tableBody.length) throw 'Row span for column ' + columnIndex + ' (with indexes starting from 0) exceeded row count'; return tableBody[endingRow][columnIndex]; } return null; } }; // lists LayoutBuilder.prototype.processList = function(orderedList, node) { var self = this, items = orderedList ? node.ol : node.ul, gapSize = node._gapSize; this.writer.context().addMargin(gapSize.width); var nextMarker; this.tracker.auto('lineAdded', addMarkerToFirstLeaf, function() { items.forEach(function(item) { nextMarker = item.listMarker; self.processNode(item); addAll(node.positions, item.positions); }); }); this.writer.context().addMargin(-gapSize.width); function addMarkerToFirstLeaf(line) { // I'm not very happy with the way list processing is implemented // (both code and algorithm should be rethinked) if (nextMarker) { var marker = nextMarker; nextMarker = null; if (marker.canvas) { var vector = marker.canvas[0]; offsetVector(vector, -marker._minWidth, 0); self.writer.addVector(vector); } else { var markerLine = new Line(self.pageSize.width); markerLine.addInline(marker._inlines[0]); markerLine.x = -marker._minWidth; markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight(); self.writer.addLine(markerLine, true); } } } }; // tables LayoutBuilder.prototype.processTable = function(tableNode) { var processor = new TableProcessor(tableNode); processor.beginTable(this.writer); for(var i = 0, l = tableNode.table.body.length; i < l; i++) { processor.beginRow(i, this.writer); var result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body, i); addAll(tableNode.positions, result.positions); processor.endRow(i, this.writer, result.pageBreaks); } processor.endTable(this.writer); }; // leafs (texts) LayoutBuilder.prototype.processLeaf = function(node) { var line = this.buildNextLine(node); while (line) { var positions = this.writer.addLine(line); node.positions.push(positions); line = this.buildNextLine(node); } }; LayoutBuilder.prototype.buildNextLine = function(textNode) { if (!textNode._inlines || textNode._inlines.length === 0) return null; var line = new Line(this.writer.context().availableWidth); while(textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline(textNode._inlines[0])) { line.addInline(textNode._inlines.shift()); } line.lastLineInParagraph = textNode._inlines.length === 0; return line; }; // images LayoutBuilder.prototype.processImage = function(node) { var position = this.writer.addImage(node); node.positions.push(position); }; LayoutBuilder.prototype.processCanvas = function(node) { var height = node._minHeight; if (this.writer.context().availableHeight < height) { // TODO: support for canvas larger than a page // TODO: support for other overflow methods this.writer.moveToNextPage(); } node.canvas.forEach(function(vector) { var position = this.writer.addVector(vector); node.positions.push(position); }, this); this.writer.context().moveDown(height); }; LayoutBuilder.prototype.processQr = function(node) { var position = this.writer.addQr(node); node.positions.push(position); }; module.exports = LayoutBuilder; /***/ }, /* 7 */ /***/ function(module, exports, __webpack_require__) { module.exports = { '4A0': [4767.87, 6740.79], '2A0': [3370.39, 4767.87], A0: [2383.94, 3370.39], A1: [1683.78, 2383.94], A2: [1190.55, 1683.78], A3: [841.89, 1190.55], A4: [595.28, 841.89], A5: [419.53, 595.28], A6: [297.64, 419.53], A7: [209.76, 297.64], A8: [147.40, 209.76], A9: [104.88, 147.40], A10: [73.70, 104.88], B0: [2834.65, 4008.19], B1: [2004.09, 2834.65], B2: [1417.32, 2004.09], B3: [1000.63, 1417.32], B4: [708.66, 1000.63], B5: [498.90, 708.66], B6: [354.33, 498.90], B7: [249.45, 354.33], B8: [175.75, 249.45], B9: [124.72, 175.75], B10: [87.87, 124.72], C0: [2599.37, 3676.54], C1: [1836.85, 2599.37], C2: [1298.27, 1836.85], C3: [918.43, 1298.27], C4: [649.13, 918.43], C5: [459.21, 649.13], C6: [323.15, 459.21], C7: [229.61, 323.15], C8: [161.57, 229.61], C9: [113.39, 161.57], C10: [79.37, 113.39], RA0: [2437.80, 3458.27], RA1: [1729.13, 2437.80], RA2: [1218.90, 1729.13], RA3: [864.57, 1218.90], RA4: [609.45, 864.57], SRA0: [2551.18, 3628.35], SRA1: [1814.17, 2551.18], SRA2: [1275.59, 1814.17], SRA3: [907.09, 1275.59], SRA4: [637.80, 907.09], EXECUTIVE: [521.86, 756.00], FOLIO: [612.00, 936.00], LEGAL: [612.00, 1008.00], LETTER: [612.00, 792.00], TABLOID: [792.00, 1224.00] }; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(Buffer) {/* jslint node: true */ 'use strict'; var pdfKit = __webpack_require__(28); var PDFImage = __webpack_require__(17); function ImageMeasure(pdfDoc, imageDictionary) { this.pdfDoc = pdfDoc; this.imageDictionary = imageDictionary || {}; } ImageMeasure.prototype.measureImage = function(src) { var image, label; var that = this; if (!this.pdfDoc._imageRegistry[src]) { label = 'I' + (++this.pdfDoc._imageCount); image = PDFImage.open(realImageSrc(src), label); image.embed(this.pdfDoc); this.pdfDoc._imageRegistry[src] = image; } else { image = this.pdfDoc._imageRegistry[src]; } return { width: image.width, height: image.height }; function realImageSrc(src) { var img = that.imageDictionary[src]; if (!img) return src; var index = img.indexOf('base64,'); if (index < 0) { throw 'invalid image format, images dictionary should contain dataURL entries'; } return new Buffer(img.substring(index + 7), 'base64'); } }; module.exports = ImageMeasure; /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4).Buffer)) /***/ }, /* 9 */ /***/ function(module, exports, __webpack_require__) { /* jslint node: true */ 'use strict'; function groupDecorations(line) { var groups = [], curGroup = null; for(var i = 0, l = line.inlines.length; i < l; i++) { var inline = line.inlines[i]; var decoration = inline.decoration; if(!decoration) { curGroup = null; continue; } var color = inline.decorationColor || inline.color || 'black'; var style = inline.decorationStyle || 'solid'; decoration = Array.isArray(decoration) ? decoration : [ decoration ]; for(var ii = 0, ll = decoration.length; ii < ll; ii++) { var deco = decoration[ii]; if(!curGroup || deco !== curGroup.decoration || style !== curGroup.decorationStyle || color !== curGroup.decorationColor || deco === 'lineThrough') { curGroup = { line: line, decoration: deco, decorationColor: color, decorationStyle: style, inlines: [ inline ] }; groups.push(curGroup); } else { curGroup.inlines.push(inline); } } } return groups; } function drawDecoration(group, x, y, pdfKitDoc) { function maxInline() { var max = 0; for (var i = 0, l = group.inlines.length; i < l; i++) { var inl = group.inlines[i]; max = inl.fontSize > max ? i : max; } return group.inlines[max]; } function width() { var sum = 0; for (var i = 0, l = group.inlines.length; i < l; i++) { sum += group.inlines[i].width; } return sum; } var firstInline = group.inlines[0], biggerInline = maxInline(), totalWidth = width(), lineAscent = group.line.getAscenderHeight(), ascent = biggerInline.font.ascender / 1000 * biggerInline.fontSize, height = biggerInline.height, descent = height - ascent; var lw = 0.5 + Math.floor(Math.max(biggerInline.fontSize - 8, 0) / 2) * 0.12; switch (group.decoration) { case 'underline': y += lineAscent + descent * 0.45; break; case 'overline': y += lineAscent - (ascent * 0.85); break; case 'lineThrough': y += lineAscent - (ascent * 0.25); break; default: throw 'Unkown decoration : ' + group.decoration; } pdfKitDoc.save(); if(group.decorationStyle === 'double') { var gap = Math.max(0.5, lw*2); pdfKitDoc .fillColor(group.decorationColor) .rect(x + firstInline.x, y-lw/2, totalWidth, lw/2).fill() .rect(x + firstInline.x, y+gap-lw/2, totalWidth, lw/2).fill(); } else if(group.decorationStyle === 'dashed') { var nbDashes = Math.ceil(totalWidth / (3.96+2.84)); var rdx = x + firstInline.x; pdfKitDoc.rect(rdx, y, totalWidth, lw).clip(); pdfKitDoc.fillColor(group.decorationColor); for (var i = 0; i < nbDashes; i++) { pdfKitDoc.rect(rdx, y-lw/2, 3.96, lw).fill(); rdx += 3.96 + 2.84; } } else if(group.decorationStyle === 'dotted') { var nbDots = Math.ceil(totalWidth / (lw*3)); var rx = x + firstInline.x; pdfKitDoc.rect(rx, y, totalWidth, lw).clip(); pdfKitDoc.fillColor(group.decorationColor); for (var ii = 0; ii < nbDots; ii++) { pdfKitDoc.rect(rx, y-lw/2, lw, lw).fill(); rx += (lw*3); } } else if(group.decorationStyle === 'wavy') { var sh = 0.7, sv = 1; var nbWaves = Math.ceil(totalWidth / (sh*2))+1; var rwx = x + firstInline.x - 1; pdfKitDoc.rect(x + firstInline.x, y-sv, totalWidth, y+sv).clip(); pdfKitDoc.lineWidth(0.24); pdfKitDoc.moveTo(rwx, y); for(var iii = 0; iii < nbWaves; iii++) { pdfKitDoc .bezierCurveTo(rwx+sh, y-sv, rwx+sh*2, y-sv, rwx+sh*3, y) .bezierCurveTo(rwx+sh*4, y+sv, rwx+sh*5, y+sv, rwx+sh*6, y); rwx += sh*6; } pdfKitDoc.stroke(group.decorationColor); } else { pdfKitDoc .fillColor(group.decorationColor) .rect(x + firstInline.x, y-lw/2, totalWidth, lw) .fill(); } pdfKitDoc.restore(); } function drawDecorations(line, x, y, pdfKitDoc) { var groups = groupDecorations(line); for (var i = 0, l = groups.length; i < l; i++) { drawDecoration(groups[i], x, y, pdfKitDoc); } } function drawBackground(line, x, y, pdfKitDoc) { var height = line.getHeight(); for(var i = 0, l = line.inlines.length; i < l; i++) { var inline = line.inlines[i]; if(inline.background) { pdfKitDoc .fillColor(inline.background) .rect(x + inline.x, y, inline.width, height) .fill(); } } } module.exports = { drawBackground: drawBackground, drawDecorations: drawDecorations }; /***/ }, /* 10 */ /***/ function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(Buffer, __dirname) {/* jslint node: true */ 'use strict'; // var b64 = require('./base64.js').base64DecToArr; function VirtualFileSystem() { this.fileSystem = {}; this.baseSystem = {}; } VirtualFileSystem.prototype.readFileSync = function(filename) { filename = fixFilename(filename); var base64content = this.baseSystem[filename]; if (base64content) { return new Buffer(base64content, 'base64'); } return this.fileSystem[filename]; }; VirtualFileSystem.prototype.writeFileSync = function(filename, content) { this.fileSystem[fixFilename(filename)] = content; }; VirtualFileSystem.prototype.bindFS = function(data) { this.baseSystem = data; }; function fixFilename(filename) { if (filename.indexOf(__dirname) === 0) { filename = filename.substring(__dirname.length); } if (filename.indexOf('/') === 0) { filename = filename.substring(1); } return filename; } module.exports = new VirtualFileSystem(); /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4).Buffer, "/")) /***/ }, /* 11 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/** * @license * lodash 3.1.0 (Custom Build) * Build: `lodash modern -d -o ./index.js` * Copyright 2012-2015 The Dojo Foundation * Based on Underscore.js 1.7.0 * Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors * Available under MIT license */ ;(function() { /** Used as a safe reference for `undefined` in pre-ES5 environments. */ var undefined; /** Used as the semantic version number. */ var VERSION = '3.1.0'; /** Used to compose bitmasks for wrapper metadata. */ var BIND_FLAG = 1, BIND_KEY_FLAG = 2, CURRY_BOUND_FLAG = 4, CURRY_FLAG = 8, CURRY_RIGHT_FLAG = 16, PARTIAL_FLAG = 32, PARTIAL_RIGHT_FLAG = 64, REARG_FLAG = 128, ARY_FLAG = 256; /** Used as default options for `_.trunc`. */ var DEFAULT_TRUNC_LENGTH = 30, DEFAULT_TRUNC_OMISSION = '...'; /** Used to detect when a function becomes hot. */ var HOT_COUNT = 150, HOT_SPAN = 16; /** Used to indicate the type of lazy iteratees. */ var LAZY_FILTER_FLAG = 0, LAZY_MAP_FLAG = 1, LAZY_WHILE_FLAG = 2; /** Used as the `TypeError` message for "Functions" methods. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** Used as the internal argument placeholder. */ var PLACEHOLDER = '__lodash_placeholder__'; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', boolTag = '[object Boolean]', dateTag = '[object Date]', errorTag = '[object Error]', funcTag = '[object Function]', mapTag = '[object Map]', numberTag = '[object Number]', objectTag = '[object Object]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', weakMapTag = '[object WeakMap]'; var arrayBufferTag = '[object ArrayBuffer]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** Used to match empty string literals in compiled template source. */ var reEmptyStringLeading = /\b__p \+= '';/g, reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; /** Used to match HTML entities and HTML characters. */ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g, reUnescapedHtml = /[&<>"'`]/g, reHasEscapedHtml = RegExp(reEscapedHtml.source), reHasUnescapedHtml = RegExp(reUnescapedHtml.source); /** Used to match template delimiters. */ var reEscape = /<%-([\s\S]+?)%>/g, reEvaluate = /<%([\s\S]+?)%>/g, reInterpolate = /<%=([\s\S]+?)%>/g; /** * Used to match ES template delimiters. * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components) * for more details. */ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; /** Used to match `RegExp` flags from their coerced string values. */ var reFlags = /\w*$/; /** Used to detect named functions. */ var reFuncName = /^\s*function[ \n\r\t]+\w/; /** Used to detect hexadecimal string values. */ var reHexPrefix = /^0[xX]/; /** Used to detect host constructors (Safari > 5). */ var reHostCtor = /^\[object .+?Constructor\]$/; /** Used to match latin-1 supplementary letters (excluding mathematical operators). */ var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g; /** Used to ensure capturing order of template delimiters. */ var reNoMatch = /($^)/; /** * Used to match `RegExp` special characters. * See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special) * for more details. */ var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, reHasRegExpChars = RegExp(reRegExpChars.source); /** Used to detect functions containing a `this` reference. */ var reThis = /\bthis\b/; /** Used to match unescaped characters in compiled string literals. */ var reUnescapedString = /['\n\r\u2028\u2029\\]/g; /** Used to match words to create compound words. */ var reWords = (function() { var upper = '[A-Z\\xc0-\\xd6\\xd8-\\xde]', lower = '[a-z\\xdf-\\xf6\\xf8-\\xff]+'; return RegExp(upper + '{2,}(?=' + upper + lower + ')|' + upper + '?' + lower + '|' + upper + '+|[0-9]+', 'g'); }()); /** Used to detect and test for whitespace. */ var whitespace = ( // Basic whitespace characters. ' \t\x0b\f\xa0\ufeff' + // Line terminators. '\n\r\u2028\u2029' + // Unicode category "Zs" space separators. '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' ); /** Used to assign default `context` object properties. */ var contextProps = [ 'Array', 'ArrayBuffer', 'Date', 'Error', 'Float32Array', 'Float64Array', 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Math', 'Number', 'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document', 'isFinite', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', 'window', 'WinRTError' ]; /** Used to make template sourceURLs easier to identify. */ var templateCounter = -1; /** Used to identify `toStringTag` values of typed arrays. */ var typedArrayTags = {}; typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true; typedArrayTags[argsTag] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; /** Used to identify `toStringTag` values supported by `_.clone`. */ var cloneableTags = {}; cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[stringTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[mapTag] = cloneableTags[setTag] = cloneableTags[weakMapTag] = false; /** Used as an internal `_.debounce` options object by `_.throttle`. */ var debounceOptions = { 'leading': false, 'maxWait': 0, 'trailing': false }; /** Used to map latin-1 supplementary letters to basic latin letters. */ var deburredLetters = { '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', '\xc7': 'C', '\xe7': 'c', '\xd0': 'D', '\xf0': 'd', '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', '\xcC': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', '\xeC': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', '\xd1': 'N', '\xf1': 'n', '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', '\xc6': 'Ae', '\xe6': 'ae', '\xde': 'Th', '\xfe': 'th', '\xdf': 'ss' }; /** Used to map characters to HTML entities. */ var htmlEscapes = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; /** Used to map HTML entities to characters. */ var htmlUnescapes = { '&': '&', '<': '<', '>': '>', '"': '"', ''': "'", '`': '`' }; /** Used to determine if values are of the language type `Object`. */ var objectTypes = { 'function': true, 'object': true }; /** Used to escape characters for inclusion in compiled string literals. */ var stringEscapes = { '\\': '\\', "'": "'", '\n': 'n', '\r': 'r', '\u2028': 'u2028', '\u2029': 'u2029' }; /** * Used as a reference to the global object. * * The `this` value is used if it is the global object to avoid Greasemonkey's * restricted `window` object, otherwise the `window` object is used. */ var root = (objectTypes[typeof window] && window !== (this && this.window)) ? window : this; /** Detect free variable `exports`. */ var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */ var freeGlobal = freeExports && freeModule && typeof global == 'object' && global; if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) { root = freeGlobal; } /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; /*--------------------------------------------------------------------------*/ /** * The base implementation of `compareAscending` which compares values and * sorts them in ascending order without guaranteeing a stable sort. * * @private * @param {*} value The value to compare to `other`. * @param {*} other The value to compare to `value`. * @returns {number} Returns the sort order indicator for `value`. */ function baseCompareAscending(value, other) { if (value !== other) { var valIsReflexive = value === value, othIsReflexive = other === other; if (value > other || !valIsReflexive || (typeof value == 'undefined' && othIsReflexive)) { return 1; } if (value < other || !othIsReflexive || (typeof other == 'undefined' && valIsReflexive)) { return -1; } } return 0; } /** * The base implementation of `_.indexOf` without support for binary searches. * * @private * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {number} [fromIndex=0] The index to search from. * @returns {number} Returns the index of the matched value, else `-1`. */ function baseIndexOf(array, value, fromIndex) { if (value !== value) { return indexOfNaN(array, fromIndex); } var index = (fromIndex || 0) - 1, length = array.length; while (++index < length) { if (array[index] === value) { return index; } } return -1; } /** * The base implementation of `_.sortBy` and `_.sortByAll` which uses `comparer` * to define the sort order of `array` and replaces criteria objects with their * corresponding values. * * @private * @param {Array} array The array to sort. * @param {Function} comparer The function to define sort order. * @returns {Array} Returns `array`. */ function baseSortBy(array, comparer) { var length = array.length; array.sort(comparer); while (length--) { array[length] = array[length].value; } return array; } /** * Converts `value` to a string if it is not one. An empty string is returned * for `null` or `undefined` values. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { if (typeof value == 'string') { return value; } return value == null ? '' : (value + ''); } /** * Used by `_.max` and `_.min` as the default callback for string values. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the code unit of the first character of the string. */ function charAtCallback(string) { return string.charCodeAt(0); } /** * Used by `_.trim` and `_.trimLeft` to get the index of the first character * of `string` that is not found in `chars`. * * @private * @param {string} string The string to inspect. * @param {string} chars The characters to find. * @returns {number} Returns the index of the first character not found in `chars`. */ function charsLeftIndex(string, chars) { var index = -1, length = string.length; while (++index < length && chars.indexOf(string.charAt(index)) > -1) {} return index; } /** * Used by `_.trim` and `_.trimRight` to get the index of the last character * of `string` that is not found in `chars`. * * @private * @param {string} string The string to inspect. * @param {string} chars The characters to find. * @returns {number} Returns the index of the last character not found in `chars`. */ function charsRightIndex(string, chars) { var index = string.length; while (index-- && chars.indexOf(string.charAt(index)) > -1) {} return index; } /** * Used by `_.sortBy` to compare transformed elements of a collection and stable * sort them in ascending order. * * @private * @param {Object} object The object to compare to `other`. * @param {Object} other The object to compare to `object`. * @returns {number} Returns the sort order indicator for `object`. */ function compareAscending(object, other) { return baseCompareAscending(object.criteria, other.criteria) || (object.index - other.index); } /** * Used by `_.sortByAll` to compare multiple properties of each element * in a collection and stable sort them in ascending order. * * @private * @param {Object} object The object to compare to `other`. * @param {Object} other The object to compare to `object`. * @returns {number} Returns the sort order indicator for `object`. */ function compareMultipleAscending(object, other) { var index = -1, objCriteria = object.criteria, othCriteria = other.criteria, length = objCriteria.length; while (++index < length) { var result = baseCompareAscending(objCriteria[index], othCriteria[index]); if (result) { return result; } } // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications // that causes it, under certain circumstances, to provide the same value for // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 // for more details. // // This also ensures a stable sort in V8 and other engines. // See https://code.google.com/p/v8/issues/detail?id=90 for more details. return object.index - other.index; } /** * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. * * @private * @param {string} letter The matched letter to deburr. * @returns {string} Returns the deburred letter. */ function deburrLetter(letter) { return deburredLetters[letter]; } /** * Used by `_.escape` to convert characters to HTML entities. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ function escapeHtmlChar(chr) { return htmlEscapes[chr]; } /** * Used by `_.template` to escape characters for inclusion in compiled * string literals. * * @private * @param {string} chr The matched character to escape. * @returns {string} Returns the escaped character. */ function escapeStringChar(chr) { return '\\' + stringEscapes[chr]; } /** * Gets the index at which the first occurrence of `NaN` is found in `array`. * If `fromRight` is provided elements of `array` are iterated from right to left. * * @private * @param {Array} array The array to search. * @param {number} [fromIndex] The index to search from. * @param {boolean} [fromRight] Specify iterating from right to left. * @returns {number} Returns the index of the matched `NaN`, else `-1`. */ function indexOfNaN(array, fromIndex, fromRight) { var length = array.length, index = fromRight ? (fromIndex || length) : ((fromIndex || 0) - 1); while ((fromRight ? index-- : ++index < length)) { var other = array[index]; if (other !== other) { return index; } } return -1; } /** * Checks if `value` is object-like. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. */ function isObjectLike(value) { return (value && typeof value == 'object') || false; } /** * Used by `trimmedLeftIndex` and `trimmedRightIndex` to determine if a * character code is whitespace. * * @private * @param {number} charCode The character code to inspect. * @returns {boolean} Returns `true` if `charCode` is whitespace, else `false`. */ function isSpace(charCode) { return ((charCode <= 160 && (charCode >= 9 && charCode <= 13) || charCode == 32 || charCode == 160) || charCode == 5760 || charCode == 6158 || (charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279))); } /** * Replaces all `placeholder` elements in `array` with an internal placeholder * and returns an array of their indexes. * * @private * @param {Array} array The array to modify. * @param {*} placeholder The placeholder to replace. * @returns {Array} Returns the new array of placeholder indexes. */ function replaceHolders(array, placeholder) { var index = -1, length = array.length, resIndex = -1, result = []; while (++index < length) { if (array[index] === placeholder) { array[index] = PLACEHOLDER; result[++resIndex] = index; } } return result; } /** * An implementation of `_.uniq` optimized for sorted arrays without support * for callback shorthands and `this` binding. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The function invoked per iteration. * @returns {Array} Returns the new duplicate-value-free array. */ function sortedUniq(array, iteratee) { var seen, index = -1, length = array.length, resIndex = -1, result = []; while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value, index, array) : value; if (!index || seen !== computed) { seen = computed; result[++resIndex] = value; } } return result; } /** * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace * character of `string`. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the index of the first non-whitespace character. */ function trimmedLeftIndex(string) { var index = -1, length = string.length; while (++index < length && isSpace(string.charCodeAt(index))) {} return index; } /** * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace * character of `string`. * * @private * @param {string} string The string to inspect. * @returns {number} Returns the index of the last non-whitespace character. */ function trimmedRightIndex(string) { var index = string.length; while (index-- && isSpace(string.charCodeAt(index))) {} return index; } /** * Used by `_.unescape` to convert HTML entities to characters. * * @private * @param {string} chr The matched character to unescape. * @returns {string} Returns the unescaped character. */ function unescapeHtmlChar(chr) { return htmlUnescapes[chr]; } /*--------------------------------------------------------------------------*/ /** * Create a new pristine `lodash` function using the given `context` object. * * @static * @memberOf _ * @category Utility * @param {Object} [context=root] The context object. * @returns {Function} Returns a new `lodash` function. * @example * * _.mixin({ 'add': function(a, b) { return a + b; } }); * * var lodash = _.runInContext(); * lodash.mixin({ 'sub': function(a, b) { return a - b; } }); * * _.isFunction(_.add); * // => true * _.isFunction(_.sub); * // => false * * lodash.isFunction(lodash.add); * // => false * lodash.isFunction(lodash.sub); * // => true * * // using `context` to mock `Date#getTime` use in `_.now` * var mock = _.runInContext({ * 'Date': function() { * return { 'getTime': getTimeMock }; * } * }); * * // or creating a suped-up `defer` in Node.js * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; */ function runInContext(context) { // Avoid issues with some ES3 environments that attempt to use values, named // after built-in constructors like `Object`, for the creation of literals. // ES5 clears this up by stating that literals must use built-in constructors. // See https://es5.github.io/#x11.1.5 for more details. context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; /** Native constructor references. */ var Array = context.Array, Date = context.Date, Error = context.Error, Function = context.Function, Math = context.Math, Number = context.Number, Object = context.Object, RegExp = context.RegExp, String = context.String, TypeError = context.TypeError; /** Used for native method references. */ var arrayProto = Array.prototype, objectProto = Object.prototype; /** Used to detect DOM support. */ var document = (document = context.window) && document.document; /** Used to resolve the decompiled source of functions. */ var fnToString = Function.prototype.toString; /** Used to the length of n-tuples for `_.unzip`. */ var getLength = baseProperty('length'); /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to generate unique IDs. */ var idCounter = 0; /** * Used to resolve the `toStringTag` of values. * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) * for more details. */ var objToString = objectProto.toString; /** Used to restore the original `_` reference in `_.noConflict`. */ var oldDash = context._; /** Used to detect if a method is native. */ var reNative = RegExp('^' + escapeRegExp(objToString) .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Native method references. */ var ArrayBuffer = isNative(ArrayBuffer = context.ArrayBuffer) && ArrayBuffer, bufferSlice = isNative(bufferSlice = ArrayBuffer && new ArrayBuffer(0).slice) && bufferSlice, ceil = Math.ceil, clearTimeout = context.clearTimeout, floor = Math.floor, getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, push = arrayProto.push, propertyIsEnumerable = objectProto.propertyIsEnumerable, Set = isNative(Set = context.Set) && Set, setTimeout = context.setTimeout, splice = arrayProto.splice, Uint8Array = isNative(Uint8Array = context.Uint8Array) && Uint8Array, unshift = arrayProto.unshift, WeakMap = isNative(WeakMap = context.WeakMap) && WeakMap; /** Used to clone array buffers. */ var Float64Array = (function() { // Safari 5 errors when using an array buffer to initialize a typed array // where the array buffer's `byteLength` is not a multiple of the typed // array's `BYTES_PER_ELEMENT`. try { var func = isNative(func = context.Float64Array) && func, result = new func(new ArrayBuffer(10), 0, 1) && func; } catch(e) {} return result; }()); /* Native method references for those with the same name as other `lodash` methods. */ var nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, nativeIsFinite = context.isFinite, nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys, nativeMax = Math.max, nativeMin = Math.min, nativeNow = isNative(nativeNow = Date.now) && nativeNow, nativeNumIsFinite = isNative(nativeNumIsFinite = Number.isFinite) && nativeNumIsFinite, nativeParseInt = context.parseInt, nativeRandom = Math.random; /** Used as references for `-Infinity` and `Infinity`. */ var NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY, POSITIVE_INFINITY = Number.POSITIVE_INFINITY; /** Used as references for the maximum length and index of an array. */ var MAX_ARRAY_LENGTH = Math.pow(2, 32) - 1, MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; /** Used as the size, in bytes, of each `Float64Array` element. */ var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0; /** * Used as the maximum length of an array-like value. * See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) * for more details. */ var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1; /** Used to store function metadata. */ var metaMap = WeakMap && new WeakMap; /*------------------------------------------------------------------------*/ /** * Creates a `lodash` object which wraps `value` to enable intuitive chaining. * Methods that operate on and return arrays, collections, and functions can * be chained together. Methods that return a boolean or single value will * automatically end the chain returning the unwrapped value. Explicit chaining * may be enabled using `_.chain`. The execution of chained methods is lazy, * that is, execution is deferred until `_#value` is implicitly or explicitly * called. * * Lazy evaluation allows several methods to support shortcut fusion. Shortcut * fusion is an optimization that merges iteratees to avoid creating intermediate * arrays and reduce the number of iteratee executions. * * Chaining is supported in custom builds as long as the `_#value` method is * directly or indirectly included in the build. * * In addition to lodash methods, wrappers also have the following `Array` methods: * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, * and `unshift` * * The wrapper functions that support shortcut fusion are: * `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, `first`, * `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, `slice`, * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `where` * * The chainable wrapper functions are: * `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, * `callback`, `chain`, `chunk`, `compact`, `concat`, `constant`, `countBy`, * `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`, * `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, `flatten`, * `flattenDeep`, `flow`, `flowRight`, `forEach`, `forEachRight`, `forIn`, * `forInRight`, `forOwn`, `forOwnRight`, `functions`, `groupBy`, `indexBy`, * `initial`, `intersection`, `invert`, `invoke`, `keys`, `keysIn`, `map`, * `mapValues`, `matches`, `memoize`, `merge`, `mixin`, `negate`, `noop`, * `omit`, `once`, `pairs`, `partial`, `partialRight`, `partition`, `pick`, * `pluck`, `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, * `rearg`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, * `sortBy`, `sortByAll`, `splice`, `take`, `takeRight`, `takeRightWhile`, * `takeWhile`, `tap`, `throttle`, `thru`, `times`, `toArray`, `toPlainObject`, * `transform`, `union`, `uniq`, `unshift`, `unzip`, `values`, `valuesIn`, * `where`, `without`, `wrap`, `xor`, `zip`, and `zipObject` * * The wrapper functions that are **not** chainable by default are: * `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`, * `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, * `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `has`, * `identity`, `includes`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, * `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, `isFinite`, * `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, * `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `max`, `min`, * `noConflict`, `now`, `pad`, `padLeft`, `padRight`, `parseInt`, `pop`, * `random`, `reduce`, `reduceRight`, `repeat`, `result`, `runInContext`, * `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, `sortedLastIndex`, * `startCase`, `startsWith`, `template`, `trim`, `trimLeft`, `trimRight`, * `trunc`, `unescape`, `uniqueId`, `value`, and `words` * * The wrapper function `sample` will return a wrapped value when `n` is provided, * otherwise an unwrapped value is returned. * * @name _ * @constructor * @category Chain * @param {*} value The value to wrap in a `lodash` instance. * @returns {Object} Returns a `lodash` instance. * @example * * var wrapped = _([1, 2, 3]); * * // returns an unwrapped value * wrapped.reduce(function(sum, n) { return sum + n; }); * // => 6 * * // returns a wrapped value * var squares = wrapped.map(function(n) { return n * n; }); * * _.isArray(squares); * // => false * * _.isArray(squares.value()); * // => true */ function lodash(value) { if (isObjectLike(value) && !isArray(value)) { if (value instanceof LodashWrapper) { return value; } if (hasOwnProperty.call(value, '__wrapped__')) { return new LodashWrapper(value.__wrapped__, value.__chain__, arrayCopy(value.__actions__)); } } return new LodashWrapper(value); } /** * The base constructor for creating `lodash` wrapper objects. * * @private * @param {*} value The value to wrap. * @param {boolean} [chainAll] Enable chaining for all wrapper methods. * @param {Array} [actions=[]] Actions to peform to resolve the unwrapped value. */ function LodashWrapper(value, chainAll, actions) { this.__actions__ = actions || []; this.__chain__ = !!chainAll; this.__wrapped__ = value; } /** * An object environment feature flags. * * @static * @memberOf _ * @type Object */ var support = lodash.support = {}; (function(x) { /** * Detect if functions can be decompiled by `Function#toString` * (all but Firefox OS certified apps, older Opera mobile browsers, and * the PlayStation 3; forced `false` for Windows 8 apps). * * @memberOf _.support * @type boolean */ support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext); /** * Detect if `Function#name` is supported (all but IE). * * @memberOf _.support * @type boolean */ support.funcNames = typeof Function.name == 'string'; /** * Detect if the DOM is supported. * * @memberOf _.support * @type boolean */ try { support.dom = document.createDocumentFragment().nodeType === 11; } catch(e) { support.dom = false; } /** * Detect if `arguments` object indexes are non-enumerable. * * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat * `arguments` object indexes as non-enumerable and fail `hasOwnProperty` * checks for indexes that exceed their function's formal parameters with * associated values of `0`. * * @memberOf _.support * @type boolean */ try { support.nonEnumArgs = !propertyIsEnumerable.call(arguments, 1); } catch(e) { support.nonEnumArgs = true; } }(0, 0)); /** * By default, the template delimiters used by lodash are like those in * embedded Ruby (ERB). Change the following template settings to use * alternative delimiters. * * @static * @memberOf _ * @type Object */ lodash.templateSettings = { /** * Used to detect `data` property values to be HTML-escaped. * * @memberOf _.templateSettings * @type RegExp */ 'escape': reEscape, /** * Used to detect code to be evaluated. * * @memberOf _.templateSettings * @type RegExp */ 'evaluate': reEvaluate, /** * Used to detect `data` property values to inject. * * @memberOf _.templateSettings * @type RegExp */ 'interpolate': reInterpolate, /** * Used to reference the data object in the template text. * * @memberOf _.templateSettings * @type string */ 'variable': '', /** * Used to import variables into the compiled template. * * @memberOf _.templateSettings * @type Object */ 'imports': { /** * A reference to the `lodash` function. * * @memberOf _.templateSettings.imports * @type Function */ '_': lodash } }; /*------------------------------------------------------------------------*/ /** * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. * * @private * @param {*} value The value to wrap. */ function LazyWrapper(value) { this.actions = null; this.dir = 1; this.dropCount = 0; this.filtered = false; this.iteratees = null; this.takeCount = POSITIVE_INFINITY; this.views = null; this.wrapped = value; } /** * Creates a clone of the lazy wrapper object. * * @private * @name clone * @memberOf LazyWrapper * @returns {Object} Returns the cloned `LazyWrapper` object. */ function lazyClone() { var actions = this.actions, iteratees = this.iteratees, views = this.views, result = new LazyWrapper(this.wrapped); result.actions = actions ? arrayCopy(actions) : null; result.dir = this.dir; result.dropCount = this.dropCount; result.filtered = this.filtered; result.iteratees = iteratees ? arrayCopy(iteratees) : null; result.takeCount = this.takeCount; result.views = views ? arrayCopy(views) : null; return result; } /** * Reverses the direction of lazy iteration. * * @private * @name reverse * @memberOf LazyWrapper * @returns {Object} Returns the new reversed `LazyWrapper` object. */ function lazyReverse() { if (this.filtered) { var result = new LazyWrapper(this); result.dir = -1; result.filtered = true; } else { result = this.clone(); result.dir *= -1; } return result; } /** * Extracts the unwrapped value from its lazy wrapper. * * @private * @name value * @memberOf LazyWrapper * @returns {*} Returns the unwrapped value. */ function lazyValue() { var array = this.wrapped.value(); if (!isArray(array)) { return baseWrapperValue(array, this.actions); } var dir = this.dir, isRight = dir < 0, view = getView(0, array.length, this.views), start = view.start, end = view.end, length = end - start, dropCount = this.dropCount, takeCount = nativeMin(length, this.takeCount - dropCount), index = isRight ? end : start - 1, iteratees = this.iteratees, iterLength = iteratees ? iteratees.length : 0, resIndex = 0, result = []; outer: while (length-- && resIndex < takeCount) { index += dir; var iterIndex = -1, value = array[index]; while (++iterIndex < iterLength) { var data = iteratees[iterIndex], iteratee = data.iteratee, computed = iteratee(value, index, array), type = data.type; if (type == LAZY_MAP_FLAG) { value = computed; } else if (!computed) { if (type == LAZY_FILTER_FLAG) { continue outer; } else { break outer; } } } if (dropCount) { dropCount--; } else { result[resIndex++] = value; } } return result; } /*------------------------------------------------------------------------*/ /** * Creates a cache object to store key/value pairs. * * @private * @static * @name Cache * @memberOf _.memoize */ function MapCache() { this.__data__ = {}; } /** * Removes `key` and its value from the cache. * * @private * @name delete * @memberOf _.memoize.Cache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed successfully, else `false`. */ function mapDelete(key) { return this.has(key) && delete this.__data__[key]; } /** * Gets the cached value for `key`. * * @private * @name get * @memberOf _.memoize.Cache * @param {string} key The key of the value to get. * @returns {*} Returns the cached value. */ function mapGet(key) { return key == '__proto__' ? undefined : this.__data__[key]; } /** * Checks if a cached value for `key` exists. * * @private * @name has * @memberOf _.memoize.Cache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function mapHas(key) { return key != '__proto__' && hasOwnProperty.call(this.__data__, key); } /** * Adds `value` to `key` of the cache. * * @private * @name set * @memberOf _.memoize.Cache * @param {string} key The key of the value to cache. * @param {*} value The value to cache. * @returns {Object} Returns the cache object. */ function mapSet(key, value) { if (key != '__proto__') { this.__data__[key] = value; } return this; } /*------------------------------------------------------------------------*/ /** * * Creates a cache object to store unique values. * * @private * @param {Array} [values] The values to cache. */ function SetCache(values) { var length = values ? values.length : 0; this.data = { 'hash': nativeCreate(null), 'set': new Set }; while (length--) { this.push(values[length]); } } /** * Checks if `value` is in `cache` mimicking the return signature of * `_.indexOf` by returning `0` if the value is found, else `-1`. * * @private * @param {Object} cache The cache to search. * @param {*} value The value to search for. * @returns {number} Returns `0` if `value` is found, else `-1`. */ function cacheIndexOf(cache, value) { var data = cache.data, result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; return result ? 0 : -1; } /** * Adds `value` to the cache. * * @private * @name push * @memberOf SetCache * @param {*} value The value to cache. */ function cachePush(value) { var data = this.data; if (typeof value == 'string' || isObject(value)) { data.set.add(value); } else { data.hash[value] = true; } } /*------------------------------------------------------------------------*/ /** * Copies the values of `source` to `array`. * * @private * @param {Array} source The array to copy values from. * @param {Array} [array=[]] The array to copy values to. * @returns {Array} Returns `array`. */ function arrayCopy(source, array) { var index = -1, length = source.length; array || (array = Array(length)); while (++index < length) { array[index] = source[index]; } return array; } /** * A specialized version of `_.forEach` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEach(array, iteratee) { var index = -1, length = array.length; while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; } /** * A specialized version of `_.forEachRight` for arrays without support for * callback shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEachRight(array, iteratee) { var length = array.length; while (length--) { if (iteratee(array[length], length, array) === false) { break; } } return array; } /** * A specialized version of `_.every` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. */ function arrayEvery(array, predicate) { var index = -1, length = array.length; while (++index < length) { if (!predicate(array[index], index, array)) { return false; } } return true; } /** * A specialized version of `_.filter` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function arrayFilter(array, predicate) { var index = -1, length = array.length, resIndex = -1, result = []; while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result[++resIndex] = value; } } return result; } /** * A specialized version of `_.map` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function arrayMap(array, iteratee) { var index = -1, length = array.length, result = Array(length); while (++index < length) { result[index] = iteratee(array[index], index, array); } return result; } /** * A specialized version of `_.max` for arrays without support for iteratees. * * @private * @param {Array} array The array to iterate over. * @returns {*} Returns the maximum value. */ function arrayMax(array) { var index = -1, length = array.length, result = NEGATIVE_INFINITY; while (++index < length) { var value = array[index]; if (value > result) { result = value; } } return result; } /** * A specialized version of `_.min` for arrays without support for iteratees. * * @private * @param {Array} array The array to iterate over. * @returns {*} Returns the minimum value. */ function arrayMin(array) { var index = -1, length = array.length, result = POSITIVE_INFINITY; while (++index < length) { var value = array[index]; if (value < result) { result = value; } } return result; } /** * A specialized version of `_.reduce` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initFromArray] Specify using the first element of `array` * as the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initFromArray) { var index = -1, length = array.length; if (initFromArray && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * A specialized version of `_.reduceRight` for arrays without support for * callback shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initFromArray] Specify using the last element of `array` * as the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduceRight(array, iteratee, accumulator, initFromArray) { var length = array.length; if (initFromArray && length) { accumulator = array[--length]; } while (length--) { accumulator = iteratee(accumulator, array[length], length, array); } return accumulator; } /** * A specialized version of `_.some` for arrays without support for callback * shorthands or `this` binding. * * @private * @param {Array} array The array to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function arraySome(array, predicate) { var index = -1, length = array.length; while (++index < length) { if (predicate(array[index], index, array)) { return true; } } return false; } /** * Used by `_.defaults` to customize its `_.assign` use. * * @private * @param {*} objectValue The destination object property value. * @param {*} sourceValue The source object property value. * @returns {*} Returns the value to assign to the destination object. */ function assignDefaults(objectValue, sourceValue) { return typeof objectValue == 'undefined' ? sourceValue : objectValue; } /** * Used by `_.template` to customize its `_.assign` use. * * **Note:** This method is like `assignDefaults` except that it ignores * inherited property values when checking if a property is `undefined`. * * @private * @param {*} objectValue The destination object property value. * @param {*} sourceValue The source object property value. * @param {string} key The key associated with the object and source values. * @param {Object} object The destination object. * @returns {*} Returns the value to assign to the destination object. */ function assignOwnDefaults(objectValue, sourceValue, key, object) { return (typeof objectValue == 'undefined' || !hasOwnProperty.call(object, key)) ? sourceValue : objectValue; } /** * The base implementation of `_.assign` without support for argument juggling, * multiple sources, and `this` binding `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {Function} [customizer] The function to customize assigning values. * @returns {Object} Returns the destination object. */ function baseAssign(object, source, customizer) { var props = keys(source); if (!customizer) { return baseCopy(source, object, props); } var index = -1, length = props.length while (++index < length) { var key = props[index], value = object[key], result = customizer(value, source[key], key, object, source); if ((result === result ? result !== value : value === value) || (typeof value == 'undefined' && !(key in object))) { object[key] = result; } } return object; } /** * The base implementation of `_.at` without support for strings and individual * key arguments. * * @private * @param {Array|Object} collection The collection to iterate over. * @param {number[]|string[]} [props] The property names or indexes of elements to pick. * @returns {Array} Returns the new array of picked elements. */ function baseAt(collection, props) { var index = -1, length = collection.length, isArr = isLength(length), propsLength = props.length, result = Array(propsLength); while(++index < propsLength) { var key = props[index]; if (isArr) { key = parseFloat(key); result[index] = isIndex(key, length) ? collection[key] : undefined; } else { result[index] = collection[key]; } } return result; } /** * Copies the properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Object} [object={}] The object to copy properties to. * @param {Array} props The property names to copy. * @returns {Object} Returns `object`. */ function baseCopy(source, object, props) { if (!props) { props = object; object = {}; } var index = -1, length = props.length; while (++index < length) { var key = props[index]; object[key] = source[key]; } return object; } /** * The base implementation of `_.bindAll` without support for individual * method name arguments. * * @private * @param {Object} object The object to bind and assign the bound methods to. * @param {string[]} methodNames The object method names to bind. * @returns {Object} Returns `object`. */ function baseBindAll(object, methodNames) { var index = -1, length = methodNames.length; while (++index < length) { var key = methodNames[index]; object[key] = createWrapper(object[key], BIND_FLAG, object); } return object; } /** * The base implementation of `_.callback` which supports specifying the * number of arguments to provide to `func`. * * @private * @param {*} [func=_.identity] The value to convert to a callback. * @param {*} [thisArg] The `this` binding of `func`. * @param {number} [argCount] The number of arguments to provide to `func`. * @returns {Function} Returns the callback. */ function baseCallback(func, thisArg, argCount) { var type = typeof func; if (type == 'function') { return (typeof thisArg != 'undefined' && isBindable(func)) ? bindCallback(func, thisArg, argCount) : func; } if (func == null) { return identity; } // Handle "_.property" and "_.matches" style callback shorthands. return type == 'object' ? baseMatches(func) : baseProperty(func + ''); } /** * The base implementation of `_.clone` without support for argument juggling * and `this` binding `customizer` functions. * * @private * @param {*} value The value to clone. * @param {boolean} [isDeep] Specify a deep clone. * @param {Function} [customizer] The function to customize cloning values. * @param {string} [key] The key of `value`. * @param {Object} [object] The object `value` belongs to. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates clones with source counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { var result; if (customizer) { result = object ? customizer(value, key, object) : customizer(value); } if (typeof result != 'undefined') { return result; } if (!isObject(value)) { return value; } var isArr = isArray(value); if (isArr) { result = initCloneArray(value); if (!isDeep) { return arrayCopy(value, result); } } else { var tag = objToString.call(value), isFunc = tag == funcTag; if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = initCloneObject(isFunc ? {} : value); if (!isDeep) { return baseCopy(value, result, keys(value)); } } else { return cloneableTags[tag] ? initCloneByTag(value, tag, isDeep) : (object ? value : {}); } } // Check for circular references and return corresponding clone. stackA || (stackA = []); stackB || (stackB = []); var length = stackA.length; while (length--) { if (stackA[length] == value) { return stackB[length]; } } // Add the source value to the stack of traversed objects and associate it with its clone. stackA.push(value); stackB.push(result); // Recursively populate clone (susceptible to call stack limits). (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); }); return result; } /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} prototype The object to inherit from. * @returns {Object} Returns the new object. */ var baseCreate = (function() { function Object() {} return function(prototype) { if (isObject(prototype)) { Object.prototype = prototype; var result = new Object; Object.prototype = null; } return result || context.Object(); }; }()); /** * The base implementation of `_.delay` and `_.defer` which accepts an index * of where to slice the arguments to provide to `func`. * * @private * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {Object} args The `arguments` object to slice and provide to `func`. * @returns {number} Returns the timer id. */ function baseDelay(func, wait, args, fromIndex) { if (!isFunction(func)) { throw new TypeError(FUNC_ERROR_TEXT); } return setTimeout(function() { func.apply(undefined, baseSlice(args, fromIndex)); }, wait); } /** * The base implementation of `_.difference` which accepts a single array * of values to exclude. * * @private * @param {Array} array The array to inspect. * @param {Array} values The values to exclude. * @returns {Array} Returns the new array of filtered values. */ function baseDifference(array, values) { var length = array ? array.length : 0, result = []; if (!length) { return result; } var index = -1, indexOf = getIndexOf(), isCommon = indexOf == baseIndexOf, cache = isCommon && values.length >= 200 && createCache(values), valuesLength = values.length; if (cache) { indexOf = cacheIndexOf; isCommon = false; values = cache; } outer: while (++index < length) { var value = array[index]; if (isCommon && value === value) { var valuesIndex = valuesLength; while (valuesIndex--) { if (values[valuesIndex] === value) { continue outer; } } result.push(value); } else if (indexOf(values, value) < 0) { result.push(value); } } return result; } /** * The base implementation of `_.forEach` without support for callback * shorthands and `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object|string} Returns `collection`. */ function baseEach(collection, iteratee) { var length = collection ? collection.length : 0; if (!isLength(length)) { return baseForOwn(collection, iteratee); } var index = -1, iterable = toObject(collection); while (++index < length) { if (iteratee(iterable[index], index, iterable) === false) { break; } } return collection; } /** * The base implementation of `_.forEachRight` without support for callback * shorthands and `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array|Object|string} Returns `collection`. */ function baseEachRight(collection, iteratee) { var length = collection ? collection.length : 0; if (!isLength(length)) { return baseForOwnRight(collection, iteratee); } var iterable = toObject(collection); while (length--) { if (iteratee(iterable[length], length, iterable) === false) { break; } } return collection; } /** * The base implementation of `_.every` without support for callback * shorthands or `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false` */ function baseEvery(collection, predicate) { var result = true; baseEach(collection, function(value, index, collection) { result = !!predicate(value, index, collection); return result; }); return result; } /** * The base implementation of `_.filter` without support for callback * shorthands or `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {Array} Returns the new filtered array. */ function baseFilter(collection, predicate) { var result = []; baseEach(collection, function(value, index, collection) { if (predicate(value, index, collection)) { result.push(value); } }); return result; } /** * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, * without support for callback shorthands and `this` binding, which iterates * over `collection` using the provided `eachFunc`. * * @private * @param {Array|Object|string} collection The collection to search. * @param {Function} predicate The function invoked per iteration. * @param {Function} eachFunc The function to iterate over `collection`. * @param {boolean} [retKey] Specify returning the key of the found element * instead of the element itself. * @returns {*} Returns the found element or its key, else `undefined`. */ function baseFind(collection, predicate, eachFunc, retKey) { var result; eachFunc(collection, function(value, key, collection) { if (predicate(value, key, collection)) { result = retKey ? key : value; return false; } }); return result; } /** * The base implementation of `_.flatten` with added support for restricting * flattening and specifying the start index. * * @private * @param {Array} array The array to flatten. * @param {boolean} [isDeep] Specify a deep flatten. * @param {boolean} [isStrict] Restrict flattening to arrays and `arguments` objects. * @param {number} [fromIndex=0] The index to start from. * @returns {Array} Returns the new flattened array. */ function baseFlatten(array, isDeep, isStrict, fromIndex) { var index = (fromIndex || 0) - 1, length = array.length, resIndex = -1, result = []; while (++index < length) { var value = array[index]; if (isObjectLike(value) && isLength(value.length) && (isArray(value) || isArguments(value))) { if (isDeep) { // Recursively flatten arrays (susceptible to call stack limits). value = baseFlatten(value, isDeep, isStrict); } var valIndex = -1, valLength = value.length; result.length += valLength; while (++valIndex < valLength) { result[++resIndex] = value[valIndex]; } } else if (!isStrict) { result[++resIndex] = value; } } return result; } /** * The base implementation of `baseForIn` and `baseForOwn` which iterates * over `object` properties returned by `keysFunc` invoking `iteratee` for * each property. Iterator functions may exit iteration early by explicitly * returning `false`. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ function baseFor(object, iteratee, keysFunc) { var index = -1, iterable = toObject(object), props = keysFunc(object), length = props.length; while (++index < length) { var key = props[index]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; } /** * This function is like `baseFor` except that it iterates over properties * in the opposite order. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {Function} keysFunc The function to get the keys of `object`. * @returns {Object} Returns `object`. */ function baseForRight(object, iteratee, keysFunc) { var iterable = toObject(object), props = keysFunc(object), length = props.length; while (length--) { var key = props[length]; if (iteratee(iterable[key], key, iterable) === false) { break; } } return object; } /** * The base implementation of `_.forIn` without support for callback * shorthands and `this` binding. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForIn(object, iteratee) { return baseFor(object, iteratee, keysIn); } /** * The base implementation of `_.forOwn` without support for callback * shorthands and `this` binding. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwn(object, iteratee) { return baseFor(object, iteratee, keys); } /** * The base implementation of `_.forOwnRight` without support for callback * shorthands and `this` binding. * * @private * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Object} Returns `object`. */ function baseForOwnRight(object, iteratee) { return baseForRight(object, iteratee, keys); } /** * The base implementation of `_.functions` which creates an array of * `object` function property names filtered from those provided. * * @private * @param {Object} object The object to inspect. * @param {Array} props The property names to filter. * @returns {Array} Returns the new array of filtered property names. */ function baseFunctions(object, props) { var index = -1, length = props.length, resIndex = -1, result = []; while (++index < length) { var key = props[index]; if (isFunction(object[key])) { result[++resIndex] = key; } } return result; } /** * The base implementation of `_.invoke` which requires additional arguments * to be provided as an array of arguments rather than individually. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|string} methodName The name of the method to invoke or * the function invoked per iteration. * @param {Array} [args] The arguments to invoke the method with. * @returns {Array} Returns the array of results. */ function baseInvoke(collection, methodName, args) { var index = -1, isFunc = typeof methodName == 'function', length = collection ? collection.length : 0, result = isLength(length) ? Array(length) : []; baseEach(collection, function(value) { var func = isFunc ? methodName : (value != null && value[methodName]); result[++index] = func ? func.apply(value, args) : undefined; }); return result; } /** * The base implementation of `_.isEqual` without support for `this` binding * `customizer` functions. * * @private * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {Function} [customizer] The function to customize comparing values. * @param {boolean} [isWhere] Specify performing partial comparisons. * @param {Array} [stackA] Tracks traversed `value` objects. * @param {Array} [stackB] Tracks traversed `other` objects. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. */ function baseIsEqual(value, other, customizer, isWhere, stackA, stackB) { // Exit early for identical values. if (value === other) { // Treat `+0` vs. `-0` as not equal. return value !== 0 || (1 / value == 1 / other); } var valType = typeof value, othType = typeof other; // Exit early for unlike primitive values. if ((valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object') || value == null || other == null) { // Return `false` unless both values are `NaN`. return value !== value && other !== other; } return baseIsEqualDeep(value, other, baseIsEqual, customizer, isWhere, stackA, stackB); } /** * A specialized version of `baseIsEqual` for arrays and objects which performs * deep comparisons and tracks traversed objects enabling objects with circular * references to be compared. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Function} [customizer] The function to customize comparing objects. * @param {boolean} [isWhere] Specify performing partial comparisons. * @param {Array} [stackA=[]] Tracks traversed `value` objects. * @param {Array} [stackB=[]] Tracks traversed `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function baseIsEqualDeep(object, other, equalFunc, customizer, isWhere, stackA, stackB) { var objIsArr = isArray(object), othIsArr = isArray(other), objTag = arrayTag, othTag = arrayTag; if (!objIsArr) { objTag = objToString.call(object); if (objTag == argsTag) { objTag = objectTag; } else if (objTag != objectTag) { objIsArr = isTypedArray(object); } } if (!othIsArr) { othTag = objToString.call(other); if (othTag == argsTag) { othTag = objectTag; } else if (othTag != objectTag) { othIsArr = isTypedArray(other); } } var objIsObj = objTag == objectTag, othIsObj = othTag == objectTag, isSameTag = objTag == othTag; if (isSameTag && !(objIsArr || objIsObj)) { return equalByTag(object, other, objTag); } var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); if (valWrapped || othWrapped) { return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isWhere, stackA, stackB); } if (!isSameTag) { return false; } // Assume cyclic values are equal. // For more information on detecting circular references see https://es5.github.io/#JO. stackA || (stackA = []); stackB || (stackB = []); var length = stackA.length; while (length--) { if (stackA[length] == object) { return stackB[length] == other; } } // Add `object` and `other` to the stack of traversed objects. stackA.push(object); stackB.push(other); var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isWhere, stackA, stackB); stackA.pop(); stackB.pop(); return result; } /** * The base implementation of `_.isMatch` without support for callback * shorthands or `this` binding. * * @private * @param {Object} source The object to inspect. * @param {Array} props The source property names to match. * @param {Array} values The source values to match. * @param {Array} strictCompareFlags Strict comparison flags for source values. * @param {Function} [customizer] The function to customize comparing objects. * @returns {boolean} Returns `true` if `object` is a match, else `false`. */ function baseIsMatch(object, props, values, strictCompareFlags, customizer) { var length = props.length; if (object == null) { return !length; } var index = -1, noCustomizer = !customizer; while (++index < length) { if ((noCustomizer && strictCompareFlags[index]) ? values[index] !== object[props[index]] : !hasOwnProperty.call(object, props[index]) ) { return false; } } index = -1; while (++index < length) { var key = props[index]; if (noCustomizer && strictCompareFlags[index]) { var result = hasOwnProperty.call(object, key); } else { var objValue = object[key], srcValue = values[index]; result = customizer ? customizer(objValue, srcValue, key) : undefined; if (typeof result == 'undefined') { result = baseIsEqual(srcValue, objValue, customizer, true); } } if (!result) { return false; } } return true; } /** * The base implementation of `_.map` without support for callback shorthands * or `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the new mapped array. */ function baseMap(collection, iteratee) { var result = []; baseEach(collection, function(value, key, collection) { result.push(iteratee(value, key, collection)); }); return result; } /** * The base implementation of `_.matches` which supports specifying whether * `source` should be cloned. * * @private * @param {Object} source The object of property values to match. * @returns {Function} Returns the new function. */ function baseMatches(source) { var props = keys(source), length = props.length; if (length == 1) { var key = props[0], value = source[key]; if (isStrictComparable(value)) { return function(object) { return object != null && value === object[key] && hasOwnProperty.call(object, key); }; } } var values = Array(length), strictCompareFlags = Array(length); while (length--) { value = source[props[length]]; values[length] = value; strictCompareFlags[length] = isStrictComparable(value); } return function(object) { return baseIsMatch(object, props, values, strictCompareFlags); }; } /** * The base implementation of `_.merge` without support for argument juggling, * multiple sources, and `this` binding `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {Function} [customizer] The function to customize merging properties. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates values with source counterparts. * @returns {Object} Returns the destination object. */ function baseMerge(object, source, customizer, stackA, stackB) { var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source)); (isSrcArr ? arrayEach : baseForOwn)(source, function(srcValue, key, source) { if (isObjectLike(srcValue)) { stackA || (stackA = []); stackB || (stackB = []); return baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB); } var value = object[key], result = customizer ? customizer(value, srcValue, key, object, source) : undefined, isCommon = typeof result == 'undefined'; if (isCommon) { result = srcValue; } if ((isSrcArr || typeof result != 'undefined') && (isCommon || (result === result ? result !== value : value === value))) { object[key] = result; } }); return object; } /** * A specialized version of `baseMerge` for arrays and objects which performs * deep merges and tracks traversed objects enabling objects with circular * references to be merged. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @param {string} key The key of the value to merge. * @param {Function} mergeFunc The function to merge values. * @param {Function} [customizer] The function to customize merging properties. * @param {Array} [stackA=[]] Tracks traversed source objects. * @param {Array} [stackB=[]] Associates values with source counterparts. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) { var length = stackA.length, srcValue = source[key]; while (length--) { if (stackA[length] == srcValue) { object[key] = stackB[length]; return; } } var value = object[key], result = customizer ? customizer(value, srcValue, key, object, source) : undefined, isCommon = typeof result == 'undefined'; if (isCommon) { result = srcValue; if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) { result = isArray(value) ? value : (value ? arrayCopy(value) : []); } else if (isPlainObject(srcValue) || isArguments(srcValue)) { result = isArguments(value) ? toPlainObject(value) : (isPlainObject(value) ? value : {}); } else { isCommon = false; } } // Add the source value to the stack of traversed objects and associate // it with its merged value. stackA.push(srcValue); stackB.push(result); if (isCommon) { // Recursively merge objects and arrays (susceptible to call stack limits). object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB); } else if (result === result ? result !== value : value === value) { object[key] = result; } } /** * The base implementation of `_.property` which does not coerce `key` to a string. * * @private * @param {string} key The key of the property to get. * @returns {Function} Returns the new function. */ function baseProperty(key) { return function(object) { return object == null ? undefined : object[key]; }; } /** * The base implementation of `_.pullAt` without support for individual * index arguments. * * @private * @param {Array} array The array to modify. * @param {number[]} indexes The indexes of elements to remove. * @returns {Array} Returns the new array of removed elements. */ function basePullAt(array, indexes) { var length = indexes.length, result = baseAt(array, indexes); indexes.sort(baseCompareAscending); while (length--) { var index = parseFloat(indexes[length]); if (index != previous && isIndex(index)) { var previous = index; splice.call(array, index, 1); } } return result; } /** * The base implementation of `_.random` without support for argument juggling * and returning floating-point numbers. * * @private * @param {number} min The minimum possible value. * @param {number} max The maximum possible value. * @returns {number} Returns the random number. */ function baseRandom(min, max) { return min + floor(nativeRandom() * (max - min + 1)); } /** * The base implementation of `_.reduce` and `_.reduceRight` without support * for callback shorthands or `this` binding, which iterates over `collection` * using the provided `eachFunc`. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} accumulator The initial value. * @param {boolean} initFromCollection Specify using the first or last element * of `collection` as the initial value. * @param {Function} eachFunc The function to iterate over `collection`. * @returns {*} Returns the accumulated value. */ function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { eachFunc(collection, function(value, index, collection) { accumulator = initFromCollection ? (initFromCollection = false, value) : iteratee(accumulator, value, index, collection) }); return accumulator; } /** * The base implementation of `setData` without support for hot loop detection. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var baseSetData = !metaMap ? identity : function(func, data) { metaMap.set(func, data); return func; }; /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; start = start == null ? 0 : (+start || 0); if (start < 0) { start = -start > length ? 0 : (length + start); } end = (typeof end == 'undefined' || end > length) ? length : (+end || 0); if (end < 0) { end += length; } length = start > end ? 0 : (end - start) >>> 0; start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * The base implementation of `_.some` without support for callback shorthands * or `this` binding. * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} predicate The function invoked per iteration. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. */ function baseSome(collection, predicate) { var result; baseEach(collection, function(value, index, collection) { result = predicate(value, index, collection); return !result; }); return !!result; } /** * The base implementation of `_.uniq` without support for callback shorthands * and `this` binding. * * @private * @param {Array} array The array to inspect. * @param {Function} [iteratee] The function invoked per iteration. * @returns {Array} Returns the new duplicate-value-free array. */ function baseUniq(array, iteratee) { var index = -1, indexOf = getIndexOf(), length = array.length, isCommon = indexOf == baseIndexOf, isLarge = isCommon && length >= 200, seen = isLarge && createCache(), result = []; if (seen) { indexOf = cacheIndexOf; isCommon = false; } else { isLarge = false; seen = iteratee ? [] : result; } outer: while (++index < length) { var value = array[index], computed = iteratee ? iteratee(value, index, array) : value; if (isCommon && value === value) { var seenIndex = seen.length; while (seenIndex--) { if (seen[seenIndex] === computed) { continue outer; } } if (iteratee) { seen.push(computed); } result.push(value); } else if (indexOf(seen, computed) < 0) { if (iteratee || isLarge) { seen.push(computed); } result.push(value); } } return result; } /** * The base implementation of `_.values` and `_.valuesIn` which creates an * array of `object` property values corresponding to the property names * returned by `keysFunc`. * * @private * @param {Object} object The object to query. * @param {Array} props The property names to get values for. * @returns {Object} Returns the array of property values. */ function baseValues(object, props) { var index = -1, length = props.length, result = Array(length); while (++index < length) { result[index] = object[props[index]]; } return result; } /** * The base implementation of `wrapperValue` which returns the result of * performing a sequence of actions on the unwrapped `value`, where each * successive action is supplied the return value of the previous. * * @private * @param {*} value The unwrapped value. * @param {Array} actions Actions to peform to resolve the unwrapped value. * @returns {*} Returns the resolved unwrapped value. */ function baseWrapperValue(value, actions) { var result = value; if (result instanceof LazyWrapper) { result = result.value(); } var index = -1, length = actions.length; while (++index < length) { var args = [result], action = actions[index]; push.apply(args, action.args); result = action.func.apply(action.thisArg, args); } return result; } /** * Performs a binary search of `array` to determine the index at which `value` * should be inserted into `array` in order to maintain its sort order. * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {boolean} [retHighest] Specify returning the highest, instead * of the lowest, index at which a value should be inserted into `array`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function binaryIndex(array, value, retHighest) { var low = 0, high = array ? array.length : low; if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { while (low < high) { var mid = (low + high) >>> 1, computed = array[mid]; if (retHighest ? (computed <= value) : (computed < value)) { low = mid + 1; } else { high = mid; } } return high; } return binaryIndexBy(array, value, identity, retHighest); } /** * This function is like `binaryIndex` except that it invokes `iteratee` for * `value` and each element of `array` to compute their sort ranking. The * iteratee is invoked with one argument; (value). * * @private * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function} iteratee The function invoked per iteration. * @param {boolean} [retHighest] Specify returning the highest, instead * of the lowest, index at which a value should be inserted into `array`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. */ function binaryIndexBy(array, value, iteratee, retHighest) { value = iteratee(value); var low = 0, high = array ? array.length : 0, valIsNaN = value !== value, valIsUndef = typeof value == 'undefined'; while (low < high) { var mid = floor((low + high) / 2), computed = iteratee(array[mid]), isReflexive = computed === computed; if (valIsNaN) { var setLow = isReflexive || retHighest; } else if (valIsUndef) { setLow = isReflexive && (retHighest || typeof computed != 'undefined'); } else { setLow = retHighest ? (computed <= value) : (computed < value); } if (setLow) { low = mid + 1; } else { high = mid; } } return nativeMin(high, MAX_ARRAY_INDEX); } /** * A specialized version of `baseCallback` which only supports `this` binding * and specifying the number of arguments to provide to `func`. * * @private * @param {Function} func The function to bind. * @param {*} thisArg The `this` binding of `func`. * @param {number} [argCount] The number of arguments to provide to `func`. * @returns {Function} Returns the callback. */ function bindCallback(func, thisArg, argCount) { if (typeof func != 'function') { return identity; } if (typeof thisArg == 'undefined') { return func; } switch (argCount) { case 1: return function(value) { return func.call(thisArg, value); }; case 3: return function(value, index, collection) { return func.call(thisArg, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(thisArg, accumulator, value, index, collection); }; case 5: return function(value, other, key, object, source) { return func.call(thisArg, value, other, key, object, source); }; } return function() { return func.apply(thisArg, arguments); }; } /** * Creates a clone of the given array buffer. * * @private * @param {ArrayBuffer} buffer The array buffer to clone. * @returns {ArrayBuffer} Returns the cloned array buffer. */ function bufferClone(buffer) { return bufferSlice.call(buffer, 0); } if (!bufferSlice) { // PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`. bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) { var byteLength = buffer.byteLength, floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0, offset = floatLength * FLOAT64_BYTES_PER_ELEMENT, result = new ArrayBuffer(byteLength); if (floatLength) { var view = new Float64Array(result, 0, floatLength); view.set(new Float64Array(buffer, 0, floatLength)); } if (byteLength != offset) { view = new Uint8Array(result, offset); view.set(new Uint8Array(buffer, offset)); } return result; }; } /** * Creates an array that is the composition of partially applied arguments, * placeholders, and provided arguments into a single array of arguments. * * @private * @param {Array|Object} args The provided arguments. * @param {Array} partials The arguments to prepend to those provided. * @param {Array} holders The `partials` placeholder indexes. * @returns {Array} Returns the new array of composed arguments. */ function composeArgs(args, partials, holders) { var holdersLength = holders.length, argsIndex = -1, argsLength = nativeMax(args.length - holdersLength, 0), leftIndex = -1, leftLength = partials.length, result = Array(argsLength + leftLength); while (++leftIndex < leftLength) { result[leftIndex] = partials[leftIndex]; } while (++argsIndex < holdersLength) { result[holders[argsIndex]] = args[argsIndex]; } while (argsLength--) { result[leftIndex++] = args[argsIndex++]; } return result; } /** * This function is like `composeArgs` except that the arguments composition * is tailored for `_.partialRight`. * * @private * @param {Array|Object} args The provided arguments. * @param {Array} partials The arguments to append to those provided. * @param {Array} holders The `partials` placeholder indexes. * @returns {Array} Returns the new array of composed arguments. */ function composeArgsRight(args, partials, holders) { var holdersIndex = -1, holdersLength = holders.length, argsIndex = -1, argsLength = nativeMax(args.length - holdersLength, 0), rightIndex = -1, rightLength = partials.length, result = Array(argsLength + rightLength); while (++argsIndex < argsLength) { result[argsIndex] = args[argsIndex]; } var pad = argsIndex; while (++rightIndex < rightLength) { result[pad + rightIndex] = partials[rightIndex]; } while (++holdersIndex < holdersLength) { result[pad + holders[holdersIndex]] = args[argsIndex++]; } return result; } /** * Creates a function that aggregates a collection, creating an accumulator * object composed from the results of running each element in the collection * through an iteratee. The `setter` sets the keys and values of the accumulator * object. If `initializer` is provided initializes the accumulator object. * * @private * @param {Function} setter The function to set keys and values of the accumulator object. * @param {Function} [initializer] The function to initialize the accumulator object. * @returns {Function} Returns the new aggregator function. */ function createAggregator(setter, initializer) { return function(collection, iteratee, thisArg) { var result = initializer ? initializer() : {}; iteratee = getCallback(iteratee, thisArg, 3); if (isArray(collection)) { var index = -1, length = collection.length; while (++index < length) { var value = collection[index]; setter(result, value, iteratee(value, index, collection), collection); } } else { baseEach(collection, function(value, key, collection) { setter(result, value, iteratee(value, key, collection), collection); }); } return result; }; } /** * Creates a function that assigns properties of source object(s) to a given * destination object. * * @private * @param {Function} assigner The function to assign values. * @returns {Function} Returns the new assigner function. */ function createAssigner(assigner) { return function() { var length = arguments.length, object = arguments[0]; if (length < 2 || object == null) { return object; } if (length > 3 && isIterateeCall(arguments[1], arguments[2], arguments[3])) { length = 2; } // Juggle arguments. if (length > 3 && typeof arguments[length - 2] == 'function') { var customizer = bindCallback(arguments[--length - 1], arguments[length--], 5); } else if (length > 2 && typeof arguments[length - 1] == 'function') { customizer = arguments[--length]; } var index = 0; while (++index < length) { var source = arguments[index]; if (source) { assigner(object, source, customizer); } } return object; }; } /** * Creates a function that wraps `func` and invokes it with the `this` * binding of `thisArg`. * * @private * @param {Function} func The function to bind. * @param {*} [thisArg] The `this` binding of `func`. * @returns {Function} Returns the new bound function. */ function createBindWrapper(func, thisArg) { var Ctor = createCtorWrapper(func); function wrapper() { return (this instanceof wrapper ? Ctor : func).apply(thisArg, arguments); } return wrapper; } /** * Creates a `Set` cache object to optimize linear searches of large arrays. * * @private * @param {Array} [values] The values to cache. * @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. */ var createCache = !(nativeCreate && Set) ? constant(null) : function(values) { return new SetCache(values); }; /** * Creates a function that produces compound words out of the words in a * given string. * * @private * @param {Function} callback The function to combine each word. * @returns {Function} Returns the new compounder function. */ function createCompounder(callback) { return function(string) { var index = -1, array = words(deburr(string)), length = array.length, result = ''; while (++index < length) { result = callback(result, array[index], index); } return result; }; } /** * Creates a function that produces an instance of `Ctor` regardless of * whether it was invoked as part of a `new` expression or by `call` or `apply`. * * @private * @param {Function} Ctor The constructor to wrap. * @returns {Function} Returns the new wrapped function. */ function createCtorWrapper(Ctor) { return function() { var thisBinding = baseCreate(Ctor.prototype), result = Ctor.apply(thisBinding, arguments); // Mimic the constructor's `return` behavior. // See https://es5.github.io/#x13.2.2 for more details. return isObject(result) ? result : thisBinding; }; } /** * Creates a function that gets the extremum value of a collection. * * @private * @param {Function} arrayFunc The function to get the extremum value from an array. * @param {boolean} [isMin] Specify returning the minimum, instead of the maximum, * extremum value. * @returns {Function} Returns the new extremum function. */ function createExtremum(arrayFunc, isMin) { return function(collection, iteratee, thisArg) { if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { iteratee = null; } var func = getCallback(), noIteratee = iteratee == null; if (!(func === baseCallback && noIteratee)) { noIteratee = false; iteratee = func(iteratee, thisArg, 3); } if (noIteratee) { var isArr = isArray(collection); if (!isArr && isString(collection)) { iteratee = charAtCallback; } else { return arrayFunc(isArr ? collection : toIterable(collection)); } } return extremumBy(collection, iteratee, isMin); }; } /** * Creates a function that wraps `func` and invokes it with optional `this` * binding of, partial application, and currying. * * @private * @param {Function|string} func The function or method name to reference. * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to prepend to those provided to the new function. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [partialsRight] The arguments to append to those provided to the new function. * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { var isAry = bitmask & ARY_FLAG, isBind = bitmask & BIND_FLAG, isBindKey = bitmask & BIND_KEY_FLAG, isCurry = bitmask & CURRY_FLAG, isCurryBound = bitmask & CURRY_BOUND_FLAG, isCurryRight = bitmask & CURRY_RIGHT_FLAG; var Ctor = !isBindKey && createCtorWrapper(func), key = func; function wrapper() { // Avoid `arguments` object use disqualifying optimizations by // converting it to an array before providing it to other functions. var length = arguments.length, index = length, args = Array(length); while (index--) { args[index] = arguments[index]; } if (partials) { args = composeArgs(args, partials, holders); } if (partialsRight) { args = composeArgsRight(args, partialsRight, holdersRight); } if (isCurry || isCurryRight) { var placeholder = wrapper.placeholder, argsHolders = replaceHolders(args, placeholder); length -= argsHolders.length; if (length < arity) { var newArgPos = argPos ? arrayCopy(argPos) : null, newArity = nativeMax(arity - length, 0), newsHolders = isCurry ? argsHolders : null, newHoldersRight = isCurry ? null : argsHolders, newPartials = isCurry ? args : null, newPartialsRight = isCurry ? null : args; bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); if (!isCurryBound) { bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); } var result = createHybridWrapper(func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity); result.placeholder = placeholder; return result; } } var thisBinding = isBind ? thisArg : this; if (isBindKey) { func = thisBinding[key]; } if (argPos) { args = reorder(args, argPos); } if (isAry && ary < args.length) { args.length = ary; } return (this instanceof wrapper ? (Ctor || createCtorWrapper(func)) : func).apply(thisBinding, args); } return wrapper; } /** * Creates the pad required for `string` based on the given padding length. * The `chars` string may be truncated if the number of padding characters * exceeds the padding length. * * @private * @param {string} string The string to create padding for. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the pad for `string`. */ function createPad(string, length, chars) { var strLength = string.length; length = +length; if (strLength >= length || !nativeIsFinite(length)) { return ''; } var padLength = length - strLength; chars = chars == null ? ' ' : (chars + ''); return repeat(chars, ceil(padLength / chars.length)).slice(0, padLength); } /** * Creates a function that wraps `func` and invokes it with the optional `this` * binding of `thisArg` and the `partials` prepended to those provided to * the wrapper. * * @private * @param {Function} func The function to partially apply arguments to. * @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. * @param {*} thisArg The `this` binding of `func`. * @param {Array} partials The arguments to prepend to those provided to the new function. * @returns {Function} Returns the new bound function. */ function createPartialWrapper(func, bitmask, thisArg, partials) { var isBind = bitmask & BIND_FLAG, Ctor = createCtorWrapper(func); function wrapper() { // Avoid `arguments` object use disqualifying optimizations by // converting it to an array before providing it `func`. var argsIndex = -1, argsLength = arguments.length, leftIndex = -1, leftLength = partials.length, args = Array(argsLength + leftLength); while (++leftIndex < leftLength) { args[leftIndex] = partials[leftIndex]; } while (argsLength--) { args[leftIndex++] = arguments[++argsIndex]; } return (this instanceof wrapper ? Ctor : func).apply(isBind ? thisArg : this, args); } return wrapper; } /** * Creates a function that either curries or invokes `func` with optional * `this` binding and partially applied arguments. * * @private * @param {Function|string} func The function or method name to reference. * @param {number} bitmask The bitmask of flags. * The bitmask may be composed of the following flags: * 1 - `_.bind` * 2 - `_.bindKey` * 4 - `_.curry` or `_.curryRight` of a bound function * 8 - `_.curry` * 16 - `_.curryRight` * 32 - `_.partial` * 64 - `_.partialRight` * 128 - `_.rearg` * 256 - `_.ary` * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partials] The arguments to be partially applied. * @param {Array} [holders] The `partials` placeholder indexes. * @param {Array} [argPos] The argument positions of the new function. * @param {number} [ary] The arity cap of `func`. * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new wrapped function. */ function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { var isBindKey = bitmask & BIND_KEY_FLAG; if (!isBindKey && !isFunction(func)) { throw new TypeError(FUNC_ERROR_TEXT); } var length = partials ? partials.length : 0; if (!length) { bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); partials = holders = null; } length -= (holders ? holders.length : 0); if (bitmask & PARTIAL_RIGHT_FLAG) { var partialsRight = partials, holdersRight = holders; partials = holders = null; } var data = !isBindKey && getData(func), newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; if (data && data !== true) { mergeData(newData, data); bitmask = newData[1]; arity = newData[9]; } newData[9] = arity == null ? (isBindKey ? 0 : func.length) : (nativeMax(arity - length, 0) || 0); if (bitmask == BIND_FLAG) { var result = createBindWrapper(newData[0], newData[2]); } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { result = createPartialWrapper.apply(null, newData); } else { result = createHybridWrapper.apply(null, newData); } var setter = data ? baseSetData : setData; return setter(result, newData); } /** * A specialized version of `baseIsEqualDeep` for arrays with support for * partial deep comparisons. * * @private * @param {Array} array The array to compare. * @param {Array} other The other array to compare. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Function} [customizer] The function to customize comparing arrays. * @param {boolean} [isWhere] Specify performing partial comparisons. * @param {Array} [stackA] Tracks traversed `value` objects. * @param {Array} [stackB] Tracks traversed `other` objects. * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. */ function equalArrays(array, other, equalFunc, customizer, isWhere, stackA, stackB) { var index = -1, arrLength = array.length, othLength = other.length, result = true; if (arrLength != othLength && !(isWhere && othLength > arrLength)) { return false; } // Deep compare the contents, ignoring non-numeric properties. while (result && ++index < arrLength) { var arrValue = array[index], othValue = other[index]; result = undefined; if (customizer) { result = isWhere ? customizer(othValue, arrValue, index) : customizer(arrValue, othValue, index); } if (typeof result == 'undefined') { // Recursively compare arrays (susceptible to call stack limits). if (isWhere) { var othIndex = othLength; while (othIndex--) { othValue = other[othIndex]; result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB); if (result) { break; } } } else { result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB); } } } return !!result; } /** * A specialized version of `baseIsEqualDeep` for comparing objects of * the same `toStringTag`. * * **Note:** This function only supports comparing values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * @private * @param {Object} value The object to compare. * @param {Object} other The other object to compare. * @param {string} tag The `toStringTag` of the objects to compare. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalByTag(object, other, tag) { switch (tag) { case boolTag: case dateTag: // Coerce dates and booleans to numbers, dates to milliseconds and booleans // to `1` or `0` treating invalid dates coerced to `NaN` as not equal. return +object == +other; case errorTag: return object.name == other.name && object.message == other.message; case numberTag: // Treat `NaN` vs. `NaN` as equal. return (object != +object) ? other != +other // But, treat `-0` vs. `+0` as not equal. : (object == 0 ? ((1 / object) == (1 / other)) : object == +other); case regexpTag: case stringTag: // Coerce regexes to strings and treat strings primitives and string // objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. return object == (other + ''); } return false; } /** * A specialized version of `baseIsEqualDeep` for objects with support for * partial deep comparisons. * * @private * @param {Object} object The object to compare. * @param {Object} other The other object to compare. * @param {Function} equalFunc The function to determine equivalents of values. * @param {Function} [customizer] The function to customize comparing values. * @param {boolean} [isWhere] Specify performing partial comparisons. * @param {Array} [stackA] Tracks traversed `value` objects. * @param {Array} [stackB] Tracks traversed `other` objects. * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. */ function equalObjects(object, other, equalFunc, customizer, isWhere, stackA, stackB) { var objProps = keys(object), objLength = objProps.length, othProps = keys(other), othLength = othProps.length; if (objLength != othLength && !isWhere) { return false; } var hasCtor, index = -1; while (++index < objLength) { var key = objProps[index], result = hasOwnProperty.call(other, key); if (result) { var objValue = object[key], othValue = other[key]; result = undefined; if (customizer) { result = isWhere ? customizer(othValue, objValue, key) : customizer(objValue, othValue, key); } if (typeof result == 'undefined') { // Recursively compare objects (susceptible to call stack limits). result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isWhere, stackA, stackB); } } if (!result) { return false; } hasCtor || (hasCtor = key == 'constructor'); } if (!hasCtor) { var objCtor = object.constructor, othCtor = other.constructor; // Non `Object` object instances with different constructors are not equal. if (objCtor != othCtor && ('constructor' in object && 'constructor' in other) && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) { return false; } } return true; } /** * Gets the extremum value of `collection` invoking `iteratee` for each value * in `collection` to generate the criterion by which the value is ranked. * The `iteratee` is invoked with three arguments; (value, index, collection). * * @private * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {boolean} [isMin] Specify returning the minimum, instead of the * maximum, extremum value. * @returns {*} Returns the extremum value. */ function extremumBy(collection, iteratee, isMin) { var exValue = isMin ? POSITIVE_INFINITY : NEGATIVE_INFINITY, computed = exValue, result = computed; baseEach(collection, function(value, index, collection) { var current = iteratee(value, index, collection); if ((isMin ? current < computed : current > computed) || (current === exValue && current === result)) { computed = current; result = value; } }); return result; } /** * Gets the appropriate "callback" function. If the `_.callback` method is * customized this function returns the custom method, otherwise it returns * the `baseCallback` function. If arguments are provided the chosen function * is invoked with them and its result is returned. * * @private * @returns {Function} Returns the chosen function or its result. */ function getCallback(func, thisArg, argCount) { var result = lodash.callback || callback; result = result === callback ? baseCallback : result; return argCount ? result(func, thisArg, argCount) : result; } /** * Gets metadata for `func`. * * @private * @param {Function} func The function to query. * @returns {*} Returns the metadata for `func`. */ var getData = !metaMap ? noop : function(func) { return metaMap.get(func); }; /** * Gets the appropriate "indexOf" function. If the `_.indexOf` method is * customized this function returns the custom method, otherwise it returns * the `baseIndexOf` function. If arguments are provided the chosen function * is invoked with them and its result is returned. * * @private * @returns {Function|number} Returns the chosen function or its result. */ function getIndexOf(collection, target, fromIndex) { var result = lodash.indexOf || indexOf; result = result === indexOf ? baseIndexOf : result; return collection ? result(collection, target, fromIndex) : result; } /** * Gets the view, applying any `transforms` to the `start` and `end` positions. * * @private * @param {number} start The start of the view. * @param {number} end The end of the view. * @param {Array} [transforms] The transformations to apply to the view. * @returns {Object} Returns an object containing the `start` and `end` * positions of the view. */ function getView(start, end, transforms) { var index = -1, length = transforms ? transforms.length : 0; while (++index < length) { var data = transforms[index], size = data.size; switch (data.type) { case 'drop': start += size; break; case 'dropRight': end -= size; break; case 'take': end = nativeMin(end, start + size); break; case 'takeRight': start = nativeMax(start, end - size); break; } } return { 'start': start, 'end': end }; } /** * Initializes an array clone. * * @private * @param {Array} array The array to clone. * @returns {Array} Returns the initialized clone. */ function initCloneArray(array) { var length = array.length, result = new array.constructor(length); // Add array properties assigned by `RegExp#exec`. if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { result.index = array.index; result.input = array.input; } return result; } /** * Initializes an object clone. * * @private * @param {Object} object The object to clone. * @returns {Object} Returns the initialized clone. */ function initCloneObject(object) { var Ctor = object.constructor; if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { Ctor = Object; } return new Ctor; } /** * Initializes an object clone based on its `toStringTag`. * * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the initialized clone. */ function initCloneByTag(object, tag, isDeep) { var Ctor = object.constructor; switch (tag) { case arrayBufferTag: return bufferClone(object); case boolTag: case dateTag: return new Ctor(+object); case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: var buffer = object.buffer; return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); case numberTag: case stringTag: return new Ctor(object); case regexpTag: var result = new Ctor(object.source, reFlags.exec(object)); result.lastIndex = object.lastIndex; } return result; } /** * Checks if `func` is eligible for `this` binding. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is eligible, else `false`. */ function isBindable(func) { var support = lodash.support, result = !(support.funcNames ? func.name : support.funcDecomp); if (!result) { var source = fnToString.call(func); if (!support.funcNames) { result = !reFuncName.test(source); } if (!result) { // Check if `func` references the `this` keyword and store the result. result = reThis.test(source) || isNative(func); baseSetData(func, result); } } return result; } /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { value = +value; length = length == null ? MAX_SAFE_INTEGER : length; return value > -1 && value % 1 == 0 && value < length; } /** * Checks if the provided arguments are from an iteratee call. * * @private * @param {*} value The potential iteratee value argument. * @param {*} index The potential iteratee index or key argument. * @param {*} object The potential iteratee object argument. * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. */ function isIterateeCall(value, index, object) { if (!isObject(object)) { return false; } var type = typeof index; if (type == 'number') { var length = object.length, prereq = isLength(length) && isIndex(index, length); } else { prereq = type == 'string' && index in object; } return prereq && object[index] === value; } /** * Checks if `value` is a valid array-like length. * * **Note:** This function is based on ES `ToLength`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength) * for more details. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` if suitable for strict * equality comparisons, else `false`. */ function isStrictComparable(value) { return value === value && (value === 0 ? ((1 / value) > 0) : !isObject(value)); } /** * Merges the function metadata of `source` into `data`. * * Merging metadata reduces the number of wrappers required to invoke a function. * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` * may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` * augment function arguments, making the order in which they are executed important, * preventing the merging of metadata. However, we make an exception for a safe * common case where curried functions have `_.ary` and or `_.rearg` applied. * * @private * @param {Array} data The destination metadata. * @param {Array} source The source metadata. * @returns {Array} Returns `data`. */ function mergeData(data, source) { var bitmask = data[1], srcBitmask = source[1], newBitmask = bitmask | srcBitmask; var arityFlags = ARY_FLAG | REARG_FLAG, bindFlags = BIND_FLAG | BIND_KEY_FLAG, comboFlags = arityFlags | bindFlags | CURRY_BOUND_FLAG | CURRY_RIGHT_FLAG; var isAry = bitmask & ARY_FLAG && !(srcBitmask & ARY_FLAG), isRearg = bitmask & REARG_FLAG && !(srcBitmask & REARG_FLAG), argPos = (isRearg ? data : source)[7], ary = (isAry ? data : source)[8]; var isCommon = !(bitmask >= REARG_FLAG && srcBitmask > bindFlags) && !(bitmask > bindFlags && srcBitmask >= REARG_FLAG); var isCombo = (newBitmask >= arityFlags && newBitmask <= comboFlags) && (bitmask < REARG_FLAG || ((isRearg || isAry) && argPos.length <= ary)); // Exit early if metadata can't be merged. if (!(isCommon || isCombo)) { return data; } // Use source `thisArg` if available. if (srcBitmask & BIND_FLAG) { data[2] = source[2]; // Set when currying a bound function. newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; } // Compose partial arguments. var value = source[3]; if (value) { var partials = data[3]; data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); } // Compose partial right arguments. value = source[5]; if (value) { partials = data[5]; data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); } // Use source `argPos` if available. value = source[7]; if (value) { data[7] = arrayCopy(value); } // Use source `ary` if it's smaller. if (srcBitmask & ARY_FLAG) { data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); } // Use source `arity` if one is not provided. if (data[9] == null) { data[9] = source[9]; } // Use source `func` and merge bitmasks. data[0] = source[0]; data[1] = newBitmask; return data; } /** * A specialized version of `_.pick` that picks `object` properties specified * by the `props` array. * * @private * @param {Object} object The source object. * @param {string[]} props The property names to pick. * @returns {Object} Returns the new object. */ function pickByArray(object, props) { object = toObject(object); var index = -1, length = props.length, result = {}; while (++index < length) { var key = props[index]; if (key in object) { result[key] = object[key]; } } return result; } /** * A specialized version of `_.pick` that picks `object` properties `predicate` * returns truthy for. * * @private * @param {Object} object The source object. * @param {Function} predicate The function invoked per iteration. * @returns {Object} Returns the new object. */ function pickByCallback(object, predicate) { var result = {}; baseForIn(object, function(value, key, object) { if (predicate(value, key, object)) { result[key] = value; } }); return result; } /** * Reorder `array` according to the specified indexes where the element at * the first index is assigned as the first element, the element at * the second index is assigned as the second element, and so on. * * @private * @param {Array} array The array to reorder. * @param {Array} indexes The arranged array indexes. * @returns {Array} Returns `array`. */ function reorder(array, indexes) { var arrLength = array.length, length = nativeMin(indexes.length, arrLength), oldArray = arrayCopy(array); while (length--) { var index = indexes[length]; array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; } return array; } /** * Sets metadata for `func`. * * **Note:** If this function becomes hot, i.e. is invoked a lot in a short * period of time, it will trip its breaker and transition to an identity function * to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) * for more details. * * @private * @param {Function} func The function to associate metadata with. * @param {*} data The metadata. * @returns {Function} Returns `func`. */ var setData = (function() { var count = 0, lastCalled = 0; return function(key, value) { var stamp = now(), remaining = HOT_SPAN - (stamp - lastCalled); lastCalled = stamp; if (remaining > 0) { if (++count >= HOT_COUNT) { return key; } } else { count = 0; } return baseSetData(key, value); }; }()); /** * A fallback implementation of `_.isPlainObject` which checks if `value` * is an object created by the `Object` constructor or has a `[[Prototype]]` * of `null`. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. */ function shimIsPlainObject(value) { var Ctor, support = lodash.support; // Exit early for non `Object` objects. if (!(isObjectLike(value) && objToString.call(value) == objectTag) || (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { return false; } // IE < 9 iterates inherited properties before own properties. If the first // iterated property is an object's own property then there are no inherited // enumerable properties. var result; // In most environments an object's own properties are iterated before // its inherited properties. If the last iterated property is an object's // own property then there are no inherited enumerable properties. baseForIn(value, function(subValue, key) { result = key; }); return typeof result == 'undefined' || hasOwnProperty.call(value, result); } /** * A fallback implementation of `Object.keys` which creates an array of the * own enumerable property names of `object`. * * @private * @param {Object} object The object to inspect. * @returns {Array} Returns the array of property names. */ function shimKeys(object) { var props = keysIn(object), propsLength = props.length, length = propsLength && object.length, support = lodash.support; var allowIndexes = length && isLength(length) && (isArray(object) || (support.nonEnumArgs && isArguments(object))); var index = -1, result = []; while (++index < propsLength) { var key = props[index]; if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { result.push(key); } } return result; } /** * Converts `value` to an array-like object if it is not one. * * @private * @param {*} value The value to process. * @returns {Array|Object} Returns the array-like object. */ function toIterable(value) { if (value == null) { return []; } if (!isLength(value.length)) { return values(value); } return isObject(value) ? value : Object(value); } /** * Converts `value` to an object if it is not one. * * @private * @param {*} value The value to process. * @returns {Object} Returns the object. */ function toObject(value) { return isObject(value) ? value : Object(value); } /*------------------------------------------------------------------------*/ /** * Creates an array of elements split into groups the length of `size`. * If `collection` can't be split evenly, the final chunk will be the remaining * elements. * * @static * @memberOf _ * @category Array * @param {Array} array The array to process. * @param {numer} [size=1] The length of each chunk. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the new array containing chunks. * @example * * _.chunk(['a', 'b', 'c', 'd'], 2); * // => [['a', 'b'], ['c', 'd']] * * _.chunk(['a', 'b', 'c', 'd'], 3); * // => [['a', 'b', 'c'], ['d']] */ function chunk(array, size, guard) { if (guard ? isIterateeCall(array, size, guard) : size == null) { size = 1; } else { size = nativeMax(+size || 1, 1); } var index = 0, length = array ? array.length : 0, resIndex = -1, result = Array(ceil(length / size)); while (index < length) { result[++resIndex] = baseSlice(array, index, (index += size)); } return result; } /** * Creates an array with all falsey values removed. The values `false`, `null`, * `0`, `""`, `undefined`, and `NaN` are falsey. * * @static * @memberOf _ * @category Array * @param {Array} array The array to compact. * @returns {Array} Returns the new array of filtered values. * @example * * _.compact([0, 1, false, 2, '', 3]); * // => [1, 2, 3] */ function compact(array) { var index = -1, length = array ? array.length : 0, resIndex = -1, result = []; while (++index < length) { var value = array[index]; if (value) { result[++resIndex] = value; } } return result; } /** * Creates an array excluding all values of the provided arrays using * `SameValueZero` for equality comparisons. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The arrays of values to exclude. * @returns {Array} Returns the new array of filtered values. * @example * * _.difference([1, 2, 3], [5, 2, 10]); * // => [1, 3] */ function difference() { var index = -1, length = arguments.length; while (++index < length) { var value = arguments[index]; if (isArray(value) || isArguments(value)) { break; } } return baseDifference(value, baseFlatten(arguments, false, true, ++index)); } /** * Creates a slice of `array` with `n` elements dropped from the beginning. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.drop([1, 2, 3]); * // => [2, 3] * * _.drop([1, 2, 3], 2); * // => [3] * * _.drop([1, 2, 3], 5); * // => [] * * _.drop([1, 2, 3], 0); * // => [1, 2, 3] */ function drop(array, n, guard) { var length = array ? array.length : 0; if (!length) { return []; } if (guard ? isIterateeCall(array, n, guard) : n == null) { n = 1; } return baseSlice(array, n < 0 ? 0 : n); } /** * Creates a slice of `array` with `n` elements dropped from the end. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to drop. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.dropRight([1, 2, 3]); * // => [1, 2] * * _.dropRight([1, 2, 3], 2); * // => [1] * * _.dropRight([1, 2, 3], 5); * // => [] * * _.dropRight([1, 2, 3], 0); * // => [1, 2, 3] */ function dropRight(array, n, guard) { var length = array ? array.length : 0; if (!length) { return []; } if (guard ? isIterateeCall(array, n, guard) : n == null) { n = 1; } n = length - (+n || 0); return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` excluding elements dropped from the end. * Elements are dropped until `predicate` returns falsey. The predicate is * bound to `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per element. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the slice of `array`. * @example * * _.dropRightWhile([1, 2, 3], function(n) { return n > 1; }); * // => [1] * * var users = [ * { 'user': 'barney', 'status': 'busy', 'active': false }, * { 'user': 'fred', 'status': 'busy', 'active': true }, * { 'user': 'pebbles', 'status': 'away', 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.dropRightWhile(users, 'active'), 'user'); * // => ['barney'] * * // using the "_.matches" callback shorthand * _.pluck(_.dropRightWhile(users, { 'status': 'away' }), 'user'); * // => ['barney', 'fred'] */ function dropRightWhile(array, predicate, thisArg) { var length = array ? array.length : 0; if (!length) { return []; } predicate = getCallback(predicate, thisArg, 3); while (length-- && predicate(array[length], length, array)) {} return baseSlice(array, 0, length + 1); } /** * Creates a slice of `array` excluding elements dropped from the beginning. * Elements are dropped until `predicate` returns falsey. The predicate is * bound to `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per element. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the slice of `array`. * @example * * _.dropWhile([1, 2, 3], function(n) { return n < 3; }); * // => [3] * * var users = [ * { 'user': 'barney', 'status': 'busy', 'active': true }, * { 'user': 'fred', 'status': 'busy', 'active': false }, * { 'user': 'pebbles', 'status': 'away', 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.dropWhile(users, 'active'), 'user'); * // => ['fred', 'pebbles'] * * // using the "_.matches" callback shorthand * _.pluck(_.dropWhile(users, { 'status': 'busy' }), 'user'); * // => ['pebbles'] */ function dropWhile(array, predicate, thisArg) { var length = array ? array.length : 0; if (!length) { return []; } var index = -1; predicate = getCallback(predicate, thisArg, 3); while (++index < length && predicate(array[index], index, array)) {} return baseSlice(array, index); } /** * This method is like `_.find` except that it returns the index of the first * element `predicate` returns truthy for, instead of the element itself. * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * _.findIndex(users, function(chr) { return chr.age < 40; }); * // => 0 * * // using the "_.matches" callback shorthand * _.findIndex(users, { 'age': 1 }); * // => 2 * * // using the "_.property" callback shorthand * _.findIndex(users, 'active'); * // => 1 */ function findIndex(array, predicate, thisArg) { var index = -1, length = array ? array.length : 0; predicate = getCallback(predicate, thisArg, 3); while (++index < length) { if (predicate(array[index], index, array)) { return index; } } return -1; } /** * This method is like `_.findIndex` except that it iterates over elements * of `collection` from right to left. * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {number} Returns the index of the found element, else `-1`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': true }, * { 'user': 'fred', 'age': 40, 'active': false }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * _.findLastIndex(users, function(chr) { return chr.age < 40; }); * // => 2 * * // using the "_.matches" callback shorthand * _.findLastIndex(users, { 'age': 40 }); * // => 1 * * // using the "_.property" callback shorthand * _.findLastIndex(users, 'active'); * // => 0 */ function findLastIndex(array, predicate, thisArg) { var length = array ? array.length : 0; predicate = getCallback(predicate, thisArg, 3); while (length--) { if (predicate(array[length], length, array)) { return length; } } return -1; } /** * Gets the first element of `array`. * * @static * @memberOf _ * @alias head * @category Array * @param {Array} array The array to query. * @returns {*} Returns the first element of `array`. * @example * * _.first([1, 2, 3]); * // => 1 * * _.first([]); * // => undefined */ function first(array) { return array ? array[0] : undefined; } /** * Flattens a nested array. If `isDeep` is `true` the array is recursively * flattened, otherwise it is only flattened a single level. * * @static * @memberOf _ * @category Array * @param {Array} array The array to flatten. * @param {boolean} [isDeep] Specify a deep flatten. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the new flattened array. * @example * * _.flatten([1, [2], [3, [[4]]]]); * // => [1, 2, 3, [[4]]]; * * // using `isDeep` * _.flatten([1, [2], [3, [[4]]]], true); * // => [1, 2, 3, 4]; */ function flatten(array, isDeep, guard) { var length = array ? array.length : 0; if (guard && isIterateeCall(array, isDeep, guard)) { isDeep = false; } return length ? baseFlatten(array, isDeep) : []; } /** * Recursively flattens a nested array. * * @static * @memberOf _ * @category Array * @param {Array} array The array to recursively flatten. * @returns {Array} Returns the new flattened array. * @example * * _.flattenDeep([1, [2], [3, [[4]]]]); * // => [1, 2, 3, 4]; */ function flattenDeep(array) { var length = array ? array.length : 0; return length ? baseFlatten(array, true) : []; } /** * Gets the index at which the first occurrence of `value` is found in `array` * using `SameValueZero` for equality comparisons. If `fromIndex` is negative, * it is used as the offset from the end of `array`. If `array` is sorted * providing `true` for `fromIndex` performs a faster binary search. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {boolean|number} [fromIndex=0] The index to search from or `true` * to perform a binary search on a sorted array. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.indexOf([1, 2, 3, 1, 2, 3], 2); * // => 1 * * // using `fromIndex` * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 4 * * // performing a binary search * _.indexOf([4, 4, 5, 5, 6, 6], 5, true); * // => 2 */ function indexOf(array, value, fromIndex) { var length = array ? array.length : 0; if (!length) { return -1; } if (typeof fromIndex == 'number') { fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); } else if (fromIndex) { var index = binaryIndex(array, value), other = array[index]; return (value === value ? value === other : other !== other) ? index : -1; } return baseIndexOf(array, value, fromIndex); } /** * Gets all but the last element of `array`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.initial([1, 2, 3]); * // => [1, 2] */ function initial(array) { return dropRight(array, 1); } /** * Creates an array of unique values in all provided arrays using `SameValueZero` * for equality comparisons. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of shared values. * @example * * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]); * // => [1, 2] */ function intersection() { var args = [], argsIndex = -1, argsLength = arguments.length, caches = [], indexOf = getIndexOf(), isCommon = indexOf == baseIndexOf; while (++argsIndex < argsLength) { var value = arguments[argsIndex]; if (isArray(value) || isArguments(value)) { args.push(value); caches.push(isCommon && value.length >= 120 && createCache(argsIndex && value)); } } argsLength = args.length; var array = args[0], index = -1, length = array ? array.length : 0, result = [], seen = caches[0]; outer: while (++index < length) { value = array[index]; if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value)) < 0) { argsIndex = argsLength; while (--argsIndex) { var cache = caches[argsIndex]; if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { continue outer; } } if (seen) { seen.push(value); } result.push(value); } } return result; } /** * Gets the last element of `array`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ function last(array) { var length = array ? array.length : 0; return length ? array[length - 1] : undefined; } /** * This method is like `_.indexOf` except that it iterates over elements of * `array` from right to left. * * @static * @memberOf _ * @category Array * @param {Array} array The array to search. * @param {*} value The value to search for. * @param {boolean|number} [fromIndex=array.length-1] The index to search from * or `true` to perform a binary search on a sorted array. * @returns {number} Returns the index of the matched value, else `-1`. * @example * * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); * // => 4 * * // using `fromIndex` * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); * // => 1 * * // performing a binary search * _.lastIndexOf([4, 4, 5, 5, 6, 6], 5, true); * // => 3 */ function lastIndexOf(array, value, fromIndex) { var length = array ? array.length : 0; if (!length) { return -1; } var index = length; if (typeof fromIndex == 'number') { index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; } else if (fromIndex) { index = binaryIndex(array, value, true) - 1; var other = array[index]; return (value === value ? value === other : other !== other) ? index : -1; } if (value !== value) { return indexOfNaN(array, index, true); } while (index--) { if (array[index] === value) { return index; } } return -1; } /** * Removes all provided values from `array` using `SameValueZero` for equality * comparisons. * * **Notes:** * - Unlike `_.without`, this method mutates `array`. * - `SameValueZero` comparisons are like strict equality comparisons, e.g. `===`, * except that `NaN` matches `NaN`. See the [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {Array} array The array to modify. * @param {...*} [values] The values to remove. * @returns {Array} Returns `array`. * @example * * var array = [1, 2, 3, 1, 2, 3]; * _.pull(array, 2, 3); * console.log(array); * // => [1, 1] */ function pull() { var array = arguments[0]; if (!(array && array.length)) { return array; } var index = 0, indexOf = getIndexOf(), length = arguments.length; while (++index < length) { var fromIndex = 0, value = arguments[index]; while ((fromIndex = indexOf(array, value, fromIndex)) > -1) { splice.call(array, fromIndex, 1); } } return array; } /** * Removes elements from `array` corresponding to the given indexes and returns * an array of the removed elements. Indexes may be specified as an array of * indexes or as individual arguments. * * **Note:** Unlike `_.at`, this method mutates `array`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to modify. * @param {...(number|number[])} [indexes] The indexes of elements to remove, * specified as individual indexes or arrays of indexes. * @returns {Array} Returns the new array of removed elements. * @example * * var array = [5, 10, 15, 20]; * var evens = _.pullAt(array, [1, 3]); * * console.log(array); * // => [5, 15] * * console.log(evens); * // => [10, 20] */ function pullAt(array) { return basePullAt(array || [], baseFlatten(arguments, false, false, 1)); } /** * Removes all elements from `array` that `predicate` returns truthy for * and returns an array of the removed elements. The predicate is bound to * `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * **Note:** Unlike `_.filter`, this method mutates `array`. * * @static * @memberOf _ * @category Array * @param {Array} array The array to modify. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the new array of removed elements. * @example * * var array = [1, 2, 3, 4]; * var evens = _.remove(array, function(n) { return n % 2 == 0; }); * * console.log(array); * // => [1, 3] * * console.log(evens); * // => [2, 4] */ function remove(array, predicate, thisArg) { var index = -1, length = array ? array.length : 0, result = []; predicate = getCallback(predicate, thisArg, 3); while (++index < length) { var value = array[index]; if (predicate(value, index, array)) { result.push(value); splice.call(array, index--, 1); length--; } } return result; } /** * Gets all but the first element of `array`. * * @static * @memberOf _ * @alias tail * @category Array * @param {Array} array The array to query. * @returns {Array} Returns the slice of `array`. * @example * * _.rest([1, 2, 3]); * // => [2, 3] */ function rest(array) { return drop(array, 1); } /** * Creates a slice of `array` from `start` up to, but not including, `end`. * * **Note:** This function is used instead of `Array#slice` to support node * lists in IE < 9 and to ensure dense arrays are returned. * * @static * @memberOf _ * @category Array * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function slice(array, start, end) { var length = array ? array.length : 0; if (!length) { return []; } if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { start = 0; end = length; } return baseSlice(array, start, end); } /** * Uses a binary search to determine the lowest index at which `value` should * be inserted into `array` in order to maintain its sort order. If an iteratee * function is provided it is invoked for `value` and each element of `array` * to compute their sort ranking. The iteratee is bound to `thisArg` and * invoked with one argument; (value). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedIndex([30, 50], 40); * // => 1 * * _.sortedIndex([4, 4, 5, 5, 6, 6], 5); * // => 2 * * var dict = { 'data': { 'thirty': 30, 'forty': 40, 'fifty': 50 } }; * * // using an iteratee function * _.sortedIndex(['thirty', 'fifty'], 'forty', function(word) { * return this.data[word]; * }, dict); * // => 1 * * // using the "_.property" callback shorthand * _.sortedIndex([{ 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); * // => 1 */ function sortedIndex(array, value, iteratee, thisArg) { var func = getCallback(iteratee); return (func === baseCallback && iteratee == null) ? binaryIndex(array, value) : binaryIndexBy(array, value, func(iteratee, thisArg, 1)); } /** * This method is like `_.sortedIndex` except that it returns the highest * index at which `value` should be inserted into `array` in order to * maintain its sort order. * * @static * @memberOf _ * @category Array * @param {Array} array The sorted array to inspect. * @param {*} value The value to evaluate. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {number} Returns the index at which `value` should be inserted * into `array`. * @example * * _.sortedLastIndex([4, 4, 5, 5, 6, 6], 5); * // => 4 */ function sortedLastIndex(array, value, iteratee, thisArg) { var func = getCallback(iteratee); return (func === baseCallback && iteratee == null) ? binaryIndex(array, value, true) : binaryIndexBy(array, value, func(iteratee, thisArg, 1), true); } /** * Creates a slice of `array` with `n` elements taken from the beginning. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.take([1, 2, 3]); * // => [1] * * _.take([1, 2, 3], 2); * // => [1, 2] * * _.take([1, 2, 3], 5); * // => [1, 2, 3] * * _.take([1, 2, 3], 0); * // => [] */ function take(array, n, guard) { var length = array ? array.length : 0; if (!length) { return []; } if (guard ? isIterateeCall(array, n, guard) : n == null) { n = 1; } return baseSlice(array, 0, n < 0 ? 0 : n); } /** * Creates a slice of `array` with `n` elements taken from the end. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {number} [n=1] The number of elements to take. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Array} Returns the slice of `array`. * @example * * _.takeRight([1, 2, 3]); * // => [3] * * _.takeRight([1, 2, 3], 2); * // => [2, 3] * * _.takeRight([1, 2, 3], 5); * // => [1, 2, 3] * * _.takeRight([1, 2, 3], 0); * // => [] */ function takeRight(array, n, guard) { var length = array ? array.length : 0; if (!length) { return []; } if (guard ? isIterateeCall(array, n, guard) : n == null) { n = 1; } n = length - (+n || 0); return baseSlice(array, n < 0 ? 0 : n); } /** * Creates a slice of `array` with elements taken from the end. Elements are * taken until `predicate` returns falsey. The predicate is bound to `thisArg` * and invoked with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per element. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the slice of `array`. * @example * * _.takeRightWhile([1, 2, 3], function(n) { return n > 1; }); * // => [2, 3] * * var users = [ * { 'user': 'barney', 'status': 'busy', 'active': false }, * { 'user': 'fred', 'status': 'busy', 'active': true }, * { 'user': 'pebbles', 'status': 'away', 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.takeRightWhile(users, 'active'), 'user'); * // => ['fred', 'pebbles'] * * // using the "_.matches" callback shorthand * _.pluck(_.takeRightWhile(users, { 'status': 'away' }), 'user'); * // => ['pebbles'] */ function takeRightWhile(array, predicate, thisArg) { var length = array ? array.length : 0; if (!length) { return []; } predicate = getCallback(predicate, thisArg, 3); while (length-- && predicate(array[length], length, array)) {} return baseSlice(array, length + 1); } /** * Creates a slice of `array` with elements taken from the beginning. Elements * are taken until `predicate` returns falsey. The predicate is bound to * `thisArg` and invoked with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @type Function * @category Array * @param {Array} array The array to query. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per element. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the slice of `array`. * @example * * _.takeWhile([1, 2, 3], function(n) { return n < 3; }); * // => [1, 2] * * var users = [ * { 'user': 'barney', 'status': 'busy', 'active': true }, * { 'user': 'fred', 'status': 'busy', 'active': false }, * { 'user': 'pebbles', 'status': 'away', 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.takeWhile(users, 'active'), 'user'); * // => ['barney'] * * // using the "_.matches" callback shorthand * _.pluck(_.takeWhile(users, { 'status': 'busy' }), 'user'); * // => ['barney', 'fred'] */ function takeWhile(array, predicate, thisArg) { var length = array ? array.length : 0; if (!length) { return []; } var index = -1; predicate = getCallback(predicate, thisArg, 3); while (++index < length && predicate(array[index], index, array)) {} return baseSlice(array, 0, index); } /** * Creates an array of unique values, in order, of the provided arrays using * `SameValueZero` for equality comparisons. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of combined values. * @example * * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); * // => [1, 2, 3, 5, 4] */ function union() { return baseUniq(baseFlatten(arguments, false, true)); } /** * Creates a duplicate-value-free version of an array using `SameValueZero` * for equality comparisons. Providing `true` for `isSorted` performs a faster * search algorithm for sorted arrays. If an iteratee function is provided it * is invoked for each value in the array to generate the criterion by which * uniqueness is computed. The `iteratee` is bound to `thisArg` and invoked * with three arguments; (value, index, array). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @alias unique * @category Array * @param {Array} array The array to inspect. * @param {boolean} [isSorted] Specify the array is sorted. * @param {Function|Object|string} [iteratee] The function invoked per iteration. * If a property name or object is provided it is used to create a "_.property" * or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new duplicate-value-free array. * @example * * _.uniq([1, 2, 1]); * // => [1, 2] * * // using `isSorted` * _.uniq([1, 1, 2], true); * // => [1, 2] * * // using an iteratee function * _.uniq([1, 2.5, 1.5, 2], function(n) { return this.floor(n); }, Math); * // => [1, 2.5] * * // using the "_.property" callback shorthand * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); * // => [{ 'x': 1 }, { 'x': 2 }] */ function uniq(array, isSorted, iteratee, thisArg) { var length = array ? array.length : 0; if (!length) { return []; } // Juggle arguments. if (typeof isSorted != 'boolean' && isSorted != null) { thisArg = iteratee; iteratee = isIterateeCall(array, isSorted, thisArg) ? null : isSorted; isSorted = false; } var func = getCallback(); if (!(func === baseCallback && iteratee == null)) { iteratee = func(iteratee, thisArg, 3); } return (isSorted && getIndexOf() == baseIndexOf) ? sortedUniq(array, iteratee) : baseUniq(array, iteratee); } /** * This method is like `_.zip` except that it accepts an array of grouped * elements and creates an array regrouping the elements to their pre-`_.zip` * configuration. * * @static * @memberOf _ * @category Array * @param {Array} array The array of grouped elements to process. * @returns {Array} Returns the new array of regrouped elements. * @example * * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); * // => [['fred', 30, true], ['barney', 40, false]] * * _.unzip(zipped); * // => [['fred', 'barney'], [30, 40], [true, false]] */ function unzip(array) { var index = -1, length = (array && array.length && arrayMax(arrayMap(array, getLength))) >>> 0, result = Array(length); while (++index < length) { result[index] = arrayMap(array, baseProperty(index)); } return result; } /** * Creates an array excluding all provided values using `SameValueZero` for * equality comparisons. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @category Array * @param {Array} array The array to filter. * @param {...*} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @example * * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); * // => [2, 3, 4] */ function without(array) { return baseDifference(array, baseSlice(arguments, 1)); } /** * Creates an array that is the symmetric difference of the provided arrays. * See [Wikipedia](https://en.wikipedia.org/wiki/Symmetric_difference) for * more details. * * @static * @memberOf _ * @category Array * @param {...Array} [arrays] The arrays to inspect. * @returns {Array} Returns the new array of values. * @example * * _.xor([1, 2, 3], [5, 2, 1, 4]); * // => [3, 5, 4] * * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]); * // => [1, 4, 5] */ function xor() { var index = -1, length = arguments.length; while (++index < length) { var array = arguments[index]; if (isArray(array) || isArguments(array)) { var result = result ? baseDifference(result, array).concat(baseDifference(array, result)) : array; } } return result ? baseUniq(result) : []; } /** * Creates an array of grouped elements, the first of which contains the first * elements of the given arrays, the second of which contains the second elements * of the given arrays, and so on. * * @static * @memberOf _ * @category Array * @param {...Array} [arrays] The arrays to process. * @returns {Array} Returns the new array of grouped elements. * @example * * _.zip(['fred', 'barney'], [30, 40], [true, false]); * // => [['fred', 30, true], ['barney', 40, false]] */ function zip() { var length = arguments.length, array = Array(length); while (length--) { array[length] = arguments[length]; } return unzip(array); } /** * Creates an object composed from arrays of property names and values. Provide * either a single two dimensional array, e.g. `[[key1, value1], [key2, value2]]` * or two arrays, one of property names and one of corresponding values. * * @static * @memberOf _ * @alias object * @category Array * @param {Array} props The property names. * @param {Array} [values=[]] The property values. * @returns {Object} Returns the new object. * @example * * _.zipObject(['fred', 'barney'], [30, 40]); * // => { 'fred': 30, 'barney': 40 } */ function zipObject(props, values) { var index = -1, length = props ? props.length : 0, result = {}; if (length && !values && !isArray(props[0])) { values = []; } while (++index < length) { var key = props[index]; if (values) { result[key] = values[index]; } else if (key) { result[key[0]] = key[1]; } } return result; } /*------------------------------------------------------------------------*/ /** * Creates a `lodash` object that wraps `value` with explicit method * chaining enabled. * * @static * @memberOf _ * @category Chain * @param {*} value The value to wrap. * @returns {Object} Returns the new `lodash` object. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'pebbles', 'age': 1 } * ]; * * var youngest = _.chain(users) * .sortBy('age') * .map(function(chr) { return chr.user + ' is ' + chr.age; }) * .first() * .value(); * // => 'pebbles is 1' */ function chain(value) { var result = lodash(value); result.__chain__ = true; return result; } /** * This method invokes `interceptor` and returns `value`. The interceptor is * bound to `thisArg` and invoked with one argument; (value). The purpose of * this method is to "tap into" a method chain in order to perform operations * on intermediate results within the chain. * * @static * @memberOf _ * @category Chain * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @param {*} [thisArg] The `this` binding of `interceptor`. * @returns {*} Returns `value`. * @example * * _([1, 2, 3]) * .tap(function(array) { array.pop(); }) * .reverse() * .value(); * // => [2, 1] */ function tap(value, interceptor, thisArg) { interceptor.call(thisArg, value); return value; } /** * This method is like `_.tap` except that it returns the result of `interceptor`. * * @static * @memberOf _ * @category Chain * @param {*} value The value to provide to `interceptor`. * @param {Function} interceptor The function to invoke. * @param {*} [thisArg] The `this` binding of `interceptor`. * @returns {*} Returns the result of `interceptor`. * @example * * _([1, 2, 3]) * .last() * .thru(function(value) { return [value]; }) * .value(); * // => [3] */ function thru(value, interceptor, thisArg) { return interceptor.call(thisArg, value); } /** * Enables explicit method chaining on the wrapper object. * * @name chain * @memberOf _ * @category Chain * @returns {*} Returns the `lodash` object. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // without explicit chaining * _(users).first(); * // => { 'user': 'barney', 'age': 36 } * * // with explicit chaining * _(users).chain() * .first() * .pick('user') * .value(); * // => { 'user': 'barney' } */ function wrapperChain() { return chain(this); } /** * Reverses the wrapped array so the first element becomes the last, the * second element becomes the second to last, and so on. * * **Note:** This method mutates the wrapped array. * * @name reverse * @memberOf _ * @category Chain * @returns {Object} Returns the new reversed `lodash` object. * @example * * var array = [1, 2, 3]; * * _(array).reverse().value() * // => [3, 2, 1] * * console.log(array); * // => [3, 2, 1] */ function wrapperReverse() { var value = this.__wrapped__; if (value instanceof LazyWrapper) { if (this.__actions__.length) { value = new LazyWrapper(this); } return new LodashWrapper(value.reverse()); } return this.thru(function(value) { return value.reverse(); }); } /** * Produces the result of coercing the unwrapped value to a string. * * @name toString * @memberOf _ * @category Chain * @returns {string} Returns the coerced string value. * @example * * _([1, 2, 3]).toString(); * // => '1,2,3' */ function wrapperToString() { return (this.value() + ''); } /** * Executes the chained sequence to extract the unwrapped value. * * @name value * @memberOf _ * @alias toJSON, valueOf * @category Chain * @returns {*} Returns the resolved unwrapped value. * @example * * _([1, 2, 3]).value(); * // => [1, 2, 3] */ function wrapperValue() { return baseWrapperValue(this.__wrapped__, this.__actions__); } /*------------------------------------------------------------------------*/ /** * Creates an array of elements corresponding to the given keys, or indexes, * of `collection`. Keys may be specified as individual arguments or as arrays * of keys. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {...(number|number[]|string|string[])} [props] The property names * or indexes of elements to pick, specified individually or in arrays. * @returns {Array} Returns the new array of picked elements. * @example * * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); * // => ['a', 'c', 'e'] * * _.at(['fred', 'barney', 'pebbles'], 0, 2); * // => ['fred', 'pebbles'] */ function at(collection) { var length = collection ? collection.length : 0; if (isLength(length)) { collection = toIterable(collection); } return baseAt(collection, baseFlatten(arguments, false, false, 1)); } /** * Checks if `value` is in `collection` using `SameValueZero` for equality * comparisons. If `fromIndex` is negative, it is used as the offset from * the end of `collection`. * * **Note:** `SameValueZero` comparisons are like strict equality comparisons, * e.g. `===`, except that `NaN` matches `NaN`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) * for more details. * * @static * @memberOf _ * @alias contains, include * @category Collection * @param {Array|Object|string} collection The collection to search. * @param {*} target The value to search for. * @param {number} [fromIndex=0] The index to search from. * @returns {boolean} Returns `true` if a matching element is found, else `false`. * @example * * _.includes([1, 2, 3], 1); * // => true * * _.includes([1, 2, 3], 1, 2); * // => false * * _.includes({ 'user': 'fred', 'age': 40 }, 'fred'); * // => true * * _.includes('pebbles', 'eb'); * // => true */ function includes(collection, target, fromIndex) { var length = collection ? collection.length : 0; if (!isLength(length)) { collection = values(collection); length = collection.length; } if (!length) { return false; } if (typeof fromIndex == 'number') { fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); } else { fromIndex = 0; } return (typeof collection == 'string' || !isArray(collection) && isString(collection)) ? (fromIndex < length && collection.indexOf(target, fromIndex) > -1) : (getIndexOf(collection, target, fromIndex) > -1); } /** * Creates an object composed of keys generated from the results of running * each element of `collection` through `iteratee`. The corresponding value * of each key is the number of times the key was returned by `iteratee`. * The `iteratee` is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns the composed aggregate object. * @example * * _.countBy([4.3, 6.1, 6.4], function(n) { return Math.floor(n); }); * // => { '4': 1, '6': 2 } * * _.countBy([4.3, 6.1, 6.4], function(n) { return this.floor(n); }, Math); * // => { '4': 1, '6': 2 } * * _.countBy(['one', 'two', 'three'], 'length'); * // => { '3': 2, '5': 1 } */ var countBy = createAggregator(function(result, value, key) { hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1); }); /** * Checks if `predicate` returns truthy for **all** elements of `collection`. * The predicate is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @alias all * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {boolean} Returns `true` if all elements pass the predicate check, * else `false`. * @example * * _.every([true, 1, null, 'yes']); * // => false * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * // using the "_.property" callback shorthand * _.every(users, 'age'); * // => true * * // using the "_.matches" callback shorthand * _.every(users, { 'age': 36 }); * // => false */ function every(collection, predicate, thisArg) { var func = isArray(collection) ? arrayEvery : baseEvery; if (typeof predicate != 'function' || typeof thisArg != 'undefined') { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); } /** * Iterates over elements of `collection`, returning an array of all elements * `predicate` returns truthy for. The predicate is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @alias select * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the new filtered array. * @example * * var evens = _.filter([1, 2, 3, 4], function(n) { return n % 2 == 0; }); * // => [2, 4] * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.filter(users, 'active'), 'user'); * // => ['fred'] * * // using the "_.matches" callback shorthand * _.pluck(_.filter(users, { 'age': 36 }), 'user'); * // => ['barney'] */ function filter(collection, predicate, thisArg) { var func = isArray(collection) ? arrayFilter : baseFilter; predicate = getCallback(predicate, thisArg, 3); return func(collection, predicate); } /** * Iterates over elements of `collection`, returning the first element * `predicate` returns truthy for. The predicate is bound to `thisArg` and * invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @alias detect * @category Collection * @param {Array|Object|string} collection The collection to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {*} Returns the matched element, else `undefined`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * _.result(_.find(users, function(chr) { return chr.age < 40; }), 'user'); * // => 'barney' * * // using the "_.matches" callback shorthand * _.result(_.find(users, { 'age': 1 }), 'user'); * // => 'pebbles' * * // using the "_.property" callback shorthand * _.result(_.find(users, 'active'), 'user'); * // => 'fred' */ function find(collection, predicate, thisArg) { if (isArray(collection)) { var index = findIndex(collection, predicate, thisArg); return index > -1 ? collection[index] : undefined; } predicate = getCallback(predicate, thisArg, 3); return baseFind(collection, predicate, baseEach); } /** * This method is like `_.find` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {*} Returns the matched element, else `undefined`. * @example * * _.findLast([1, 2, 3, 4], function(n) { return n % 2 == 1; }); * // => 3 */ function findLast(collection, predicate, thisArg) { predicate = getCallback(predicate, thisArg, 3); return baseFind(collection, predicate, baseEachRight); } /** * Performs a deep comparison between each element in `collection` and the * source object, returning the first element that has equivalent property * values. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to search. * @param {Object} source The object of property values to match. * @returns {*} Returns the matched element, else `undefined`. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'status': 'busy' }, * { 'user': 'fred', 'age': 40, 'status': 'busy' } * ]; * * _.result(_.findWhere(users, { 'status': 'busy' }), 'user'); * // => 'barney' * * _.result(_.findWhere(users, { 'age': 40 }), 'user'); * // => 'fred' */ function findWhere(collection, source) { return find(collection, baseMatches(source)); } /** * Iterates over elements of `collection` invoking `iteratee` for each element. * The `iteratee` is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). Iterator functions may exit iteration early * by explicitly returning `false`. * * **Note:** As with other "Collections" methods, objects with a `length` property * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` * may be used for object iteration. * * @static * @memberOf _ * @alias each * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array|Object|string} Returns `collection`. * @example * * _([1, 2, 3]).forEach(function(n) { console.log(n); }).value(); * // => logs each value from left to right and returns the array * * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(n, key) { console.log(n, key); }); * // => logs each value-key pair and returns the object (iteration order is not guaranteed) */ function forEach(collection, iteratee, thisArg) { return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) ? arrayEach(collection, iteratee) : baseEach(collection, bindCallback(iteratee, thisArg, 3)); } /** * This method is like `_.forEach` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @alias eachRight * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array|Object|string} Returns `collection`. * @example * * _([1, 2, 3]).forEachRight(function(n) { console.log(n); }).join(','); * // => logs each value from right to left and returns the array */ function forEachRight(collection, iteratee, thisArg) { return (typeof iteratee == 'function' && typeof thisArg == 'undefined' && isArray(collection)) ? arrayEachRight(collection, iteratee) : baseEachRight(collection, bindCallback(iteratee, thisArg, 3)); } /** * Creates an object composed of keys generated from the results of running * each element of `collection` through `iteratee`. The corresponding value * of each key is an array of the elements responsible for generating the key. * The `iteratee` is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns the composed aggregate object. * @example * * _.groupBy([4.2, 6.1, 6.4], function(n) { return Math.floor(n); }); * // => { '4': [4.2], '6': [6.1, 6.4] } * * _.groupBy([4.2, 6.1, 6.4], function(n) { return this.floor(n); }, Math); * // => { '4': [4.2], '6': [6.1, 6.4] } * * // using the "_.property" callback shorthand * _.groupBy(['one', 'two', 'three'], 'length'); * // => { '3': ['one', 'two'], '5': ['three'] } */ var groupBy = createAggregator(function(result, value, key) { if (hasOwnProperty.call(result, key)) { result[key].push(value); } else { result[key] = [value]; } }); /** * Creates an object composed of keys generated from the results of running * each element of `collection` through `iteratee`. The corresponding value * of each key is the last element responsible for generating the key. The * iteratee function is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns the composed aggregate object. * @example * * var keyData = [ * { 'dir': 'left', 'code': 97 }, * { 'dir': 'right', 'code': 100 } * ]; * * _.indexBy(keyData, 'dir'); * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } * * _.indexBy(keyData, function(object) { return String.fromCharCode(object.code); }); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } * * _.indexBy(keyData, function(object) { return this.fromCharCode(object.code); }, String); * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } */ var indexBy = createAggregator(function(result, value, key) { result[key] = value; }); /** * Invokes the method named by `methodName` on each element in `collection`, * returning an array of the results of each invoked method. Any additional * arguments are provided to each invoked method. If `methodName` is a function * it is invoked for, and `this` bound to, each element in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|string} methodName The name of the method to invoke or * the function invoked per iteration. * @param {...*} [args] The arguments to invoke the method with. * @returns {Array} Returns the array of results. * @example * * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); * // => [[1, 5, 7], [1, 2, 3]] * * _.invoke([123, 456], String.prototype.split, ''); * // => [['1', '2', '3'], ['4', '5', '6']] */ function invoke(collection, methodName) { return baseInvoke(collection, methodName, baseSlice(arguments, 2)); } /** * Creates an array of values by running each element in `collection` through * `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three * arguments; (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @alias collect * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new mapped array. * @example * * _.map([1, 2, 3], function(n) { return n * 3; }); * // => [3, 6, 9] * * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(n) { return n * 3; }); * // => [3, 6, 9] (iteration order is not guaranteed) * * var users = [ * { 'user': 'barney' }, * { 'user': 'fred' } * ]; * * // using the "_.property" callback shorthand * _.map(users, 'user'); * // => ['barney', 'fred'] */ function map(collection, iteratee, thisArg) { var func = isArray(collection) ? arrayMap : baseMap; iteratee = getCallback(iteratee, thisArg, 3); return func(collection, iteratee); } /** * Gets the maximum value of `collection`. If `collection` is empty or falsey * `-Infinity` is returned. If an iteratee function is provided it is invoked * for each value in `collection` to generate the criterion by which the value * is ranked. The `iteratee` is bound to `thisArg` and invoked with three * arguments; (value, index, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee] The function invoked per iteration. * If a property name or object is provided it is used to create a "_.property" * or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {*} Returns the maximum value. * @example * * _.max([4, 2, 8, 6]); * // => 8 * * _.max([]); * // => -Infinity * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * _.max(users, function(chr) { return chr.age; }); * // => { 'user': 'fred', 'age': 40 }; * * // using the "_.property" callback shorthand * _.max(users, 'age'); * // => { 'user': 'fred', 'age': 40 }; */ var max = createExtremum(arrayMax); /** * Gets the minimum value of `collection`. If `collection` is empty or falsey * `Infinity` is returned. If an iteratee function is provided it is invoked * for each value in `collection` to generate the criterion by which the value * is ranked. The `iteratee` is bound to `thisArg` and invoked with three * arguments; (value, index, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [iteratee] The function invoked per iteration. * If a property name or object is provided it is used to create a "_.property" * or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {*} Returns the minimum value. * @example * * _.min([4, 2, 8, 6]); * // => 2 * * _.min([]); * // => Infinity * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * _.min(users, function(chr) { return chr.age; }); * // => { 'user': 'barney', 'age': 36 }; * * // using the "_.property" callback shorthand * _.min(users, 'age'); * // => { 'user': 'barney', 'age': 36 }; */ var min = createExtremum(arrayMin, true); /** * Creates an array of elements split into two groups, the first of which * contains elements `predicate` returns truthy for, while the second of which * contains elements `predicate` returns falsey for. The predicate is bound * to `thisArg` and invoked with three arguments; (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the array of grouped elements. * @example * * _.partition([1, 2, 3], function(n) { return n % 2; }); * // => [[1, 3], [2]] * * _.partition([1.2, 2.3, 3.4], function(n) { return this.floor(n) % 2; }, Math); * // => [[1, 3], [2]] * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true }, * { 'user': 'pebbles', 'age': 1, 'active': false } * ]; * * // using the "_.matches" callback shorthand * _.map(_.partition(users, { 'age': 1 }), function(array) { return _.pluck(array, 'user'); }); * // => [['pebbles'], ['barney', 'fred']] * * // using the "_.property" callback shorthand * _.map(_.partition(users, 'active'), function(array) { return _.pluck(array, 'user'); }); * // => [['fred'], ['barney', 'pebbles']] */ var partition = createAggregator(function(result, value, key) { result[key ? 0 : 1].push(value); }, function() { return [[], []]; }); /** * Gets the value of `key` from all elements in `collection`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {string} key The key of the property to pluck. * @returns {Array} Returns the property values. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 } * ]; * * _.pluck(users, 'user'); * // => ['barney', 'fred'] * * var userIndex = _.indexBy(users, 'user'); * _.pluck(userIndex, 'age'); * // => [36, 40] (iteration order is not guaranteed) */ function pluck(collection, key) { return map(collection, baseProperty(key + '')); } /** * Reduces `collection` to a value which is the accumulated result of running * each element in `collection` through `iteratee`, where each successive * invocation is supplied the return value of the previous. If `accumulator` * is not provided the first element of `collection` is used as the initial * value. The `iteratee` is bound to `thisArg`and invoked with four arguments; * (accumulator, value, index|key, collection). * * @static * @memberOf _ * @alias foldl, inject * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {*} Returns the accumulated value. * @example * * var sum = _.reduce([1, 2, 3], function(sum, n) { return sum + n; }); * // => 6 * * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, n, key) { * result[key] = n * 3; * return result; * }, {}); * // => { 'a': 3, 'b': 6, 'c': 9 } (iteration order is not guaranteed) */ function reduce(collection, iteratee, accumulator, thisArg) { var func = isArray(collection) ? arrayReduce : baseReduce; return func(collection, getCallback(iteratee, thisArg, 4), accumulator, arguments.length < 3, baseEach); } /** * This method is like `_.reduce` except that it iterates over elements of * `collection` from right to left. * * @static * @memberOf _ * @alias foldr * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {*} Returns the accumulated value. * @example * * var array = [[0, 1], [2, 3], [4, 5]]; * _.reduceRight(array, function(flattened, other) { return flattened.concat(other); }, []); * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, iteratee, accumulator, thisArg) { var func = isArray(collection) ? arrayReduceRight : baseReduce; return func(collection, getCallback(iteratee, thisArg, 4), accumulator, arguments.length < 3, baseEachRight); } /** * The opposite of `_.filter`; this method returns the elements of `collection` * that `predicate` does **not** return truthy for. * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Array} Returns the new filtered array. * @example * * var odds = _.reject([1, 2, 3, 4], function(n) { return n % 2 == 0; }); * // => [1, 3] * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.reject(users, 'active'), 'user'); * // => ['barney'] * * // using the "_.matches" callback shorthand * _.pluck(_.reject(users, { 'age': 36 }), 'user'); * // => ['fred'] */ function reject(collection, predicate, thisArg) { var func = isArray(collection) ? arrayFilter : baseFilter; predicate = getCallback(predicate, thisArg, 3); return func(collection, function(value, index, collection) { return !predicate(value, index, collection); }); } /** * Gets a random element or `n` random elements from a collection. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to sample. * @param {number} [n] The number of elements to sample. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {*} Returns the random sample(s). * @example * * _.sample([1, 2, 3, 4]); * // => 2 * * _.sample([1, 2, 3, 4], 2); * // => [3, 1] */ function sample(collection, n, guard) { if (guard ? isIterateeCall(collection, n, guard) : n == null) { collection = toIterable(collection); var length = collection.length; return length > 0 ? collection[baseRandom(0, length - 1)] : undefined; } var result = shuffle(collection); result.length = nativeMin(n < 0 ? 0 : (+n || 0), result.length); return result; } /** * Creates an array of shuffled values, using a version of the Fisher-Yates * shuffle. See [Wikipedia](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle) * for more details. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to shuffle. * @returns {Array} Returns the new shuffled array. * @example * * _.shuffle([1, 2, 3, 4]); * // => [4, 1, 3, 2] */ function shuffle(collection) { collection = toIterable(collection); var index = -1, length = collection.length, result = Array(length); while (++index < length) { var rand = baseRandom(0, index); if (index != rand) { result[index] = result[rand]; } result[rand] = collection[index]; } return result; } /** * Gets the size of `collection` by returning `collection.length` for * array-like values or the number of own enumerable properties for objects. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to inspect. * @returns {number} Returns the size of `collection`. * @example * * _.size([1, 2]); * // => 2 * * _.size({ 'one': 1, 'two': 2, 'three': 3 }); * // => 3 * * _.size('pebbles'); * // => 7 */ function size(collection) { var length = collection ? collection.length : 0; return isLength(length) ? length : keys(collection).length; } /** * Checks if `predicate` returns truthy for **any** element of `collection`. * The function returns as soon as it finds a passing value and does not iterate * over the entire collection. The predicate is bound to `thisArg` and invoked * with three arguments; (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @alias any * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {boolean} Returns `true` if any element passes the predicate check, * else `false`. * @example * * _.some([null, 0, 'yes', false], Boolean); * // => true * * var users = [ * { 'user': 'barney', 'age': 36, 'active': false }, * { 'user': 'fred', 'age': 40, 'active': true } * ]; * * // using the "_.property" callback shorthand * _.some(users, 'active'); * // => true * * // using the "_.matches" callback shorthand * _.some(users, { 'age': 1 }); * // => false */ function some(collection, predicate, thisArg) { var func = isArray(collection) ? arraySome : baseSome; if (typeof predicate != 'function' || typeof thisArg != 'undefined') { predicate = getCallback(predicate, thisArg, 3); } return func(collection, predicate); } /** * Creates an array of elements, sorted in ascending order by the results of * running each element in a collection through `iteratee`. This method performs * a stable sort, that is, it preserves the original sort order of equal elements. * The `iteratee` is bound to `thisArg` and invoked with three arguments; * (value, index|key, collection). * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {Array|Function|Object|string} [iteratee=_.identity] The function * invoked per iteration. If a property name or an object is provided it is * used to create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Array} Returns the new sorted array. * @example * * _.sortBy([1, 2, 3], function(n) { return Math.sin(n); }); * // => [3, 1, 2] * * _.sortBy([1, 2, 3], function(n) { return this.sin(n); }, Math); * // => [3, 1, 2] * * var users = [ * { 'user': 'fred' }, * { 'user': 'pebbles' }, * { 'user': 'barney' } * ]; * * // using the "_.property" callback shorthand * _.pluck(_.sortBy(users, 'user'), 'user'); * // => ['barney', 'fred', 'pebbles'] */ function sortBy(collection, iteratee, thisArg) { var index = -1, length = collection ? collection.length : 0, result = isLength(length) ? Array(length) : []; if (thisArg && isIterateeCall(collection, iteratee, thisArg)) { iteratee = null; } iteratee = getCallback(iteratee, thisArg, 3); baseEach(collection, function(value, key, collection) { result[++index] = { 'criteria': iteratee(value, key, collection), 'index': index, 'value': value }; }); return baseSortBy(result, compareAscending); } /** * This method is like `_.sortBy` except that it sorts by property names * instead of an iteratee function. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to iterate over. * @param {...(string|string[])} props The property names to sort by, * specified as individual property names or arrays of property names. * @returns {Array} Returns the new sorted array. * @example * * var users = [ * { 'user': 'barney', 'age': 36 }, * { 'user': 'fred', 'age': 40 }, * { 'user': 'barney', 'age': 26 }, * { 'user': 'fred', 'age': 30 } * ]; * * _.map(_.sortByAll(users, ['user', 'age']), _.values); * // => [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] */ function sortByAll(collection) { var args = arguments; if (args.length > 3 && isIterateeCall(args[1], args[2], args[3])) { args = [collection, args[1]]; } var index = -1, length = collection ? collection.length : 0, props = baseFlatten(args, false, false, 1), result = isLength(length) ? Array(length) : []; baseEach(collection, function(value, key, collection) { var length = props.length, criteria = Array(length); while (length--) { criteria[length] = value == null ? undefined : value[props[length]]; } result[++index] = { 'criteria': criteria, 'index': index, 'value': value }; }); return baseSortBy(result, compareMultipleAscending); } /** * Performs a deep comparison between each element in `collection` and the * source object, returning an array of all elements that have equivalent * property values. * * @static * @memberOf _ * @category Collection * @param {Array|Object|string} collection The collection to search. * @param {Object} source The object of property values to match. * @returns {Array} Returns the new filtered array. * @example * * var users = [ * { 'user': 'barney', 'age': 36, 'status': 'busy', 'pets': ['hoppy'] }, * { 'user': 'fred', 'age': 40, 'status': 'busy', 'pets': ['baby puss', 'dino'] } * ]; * * _.pluck(_.where(users, { 'age': 36 }), 'user'); * // => ['barney'] * * _.pluck(_.where(users, { 'pets': ['dino'] }), 'user'); * // => ['fred'] * * _.pluck(_.where(users, { 'status': 'busy' }), 'user'); * // => ['barney', 'fred'] */ function where(collection, source) { return filter(collection, baseMatches(source)); } /*------------------------------------------------------------------------*/ /** * Gets the number of milliseconds that have elapsed since the Unix epoch * (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @category Date * @example * * _.defer(function(stamp) { console.log(_.now() - stamp); }, _.now()); * // => logs the number of milliseconds it took for the deferred function to be invoked */ var now = nativeNow || function() { return new Date().getTime(); }; /*------------------------------------------------------------------------*/ /** * The opposite of `_.before`; this method creates a function that invokes * `func` once it is called `n` or more times. * * @static * @memberOf _ * @category Function * @param {number} n The number of calls before `func` is invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var saves = ['profile', 'settings']; * * var done = _.after(saves.length, function() { * console.log('done saving!'); * }); * * _.forEach(saves, function(type) { * asyncSave({ 'type': type, 'complete': done }); * }); * // => logs 'done saving!' after the two async saves have completed */ function after(n, func) { if (!isFunction(func)) { if (isFunction(n)) { var temp = n; n = func; func = temp; } else { throw new TypeError(FUNC_ERROR_TEXT); } } n = nativeIsFinite(n = +n) ? n : 0; return function() { if (--n < 1) { return func.apply(this, arguments); } }; } /** * Creates a function that accepts up to `n` arguments ignoring any * additional arguments. * * @static * @memberOf _ * @category Function * @param {Function} func The function to cap arguments for. * @param {number} [n=func.length] The arity cap. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Function} Returns the new function. * @example * * _.map(['6', '8', '10'], _.ary(parseInt, 1)); * // => [6, 8, 10] */ function ary(func, n, guard) { if (guard && isIterateeCall(func, n, guard)) { n = null; } n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); return createWrapper(func, ARY_FLAG, null, null, null, null, n); } /** * Creates a function that invokes `func`, with the `this` binding and arguments * of the created function, while it is called less than `n` times. Subsequent * calls to the created function return the result of the last `func` invocation. * * @static * @memberOf _ * @category Function * @param {number} n The number of calls at which `func` is no longer invoked. * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * jQuery('#add').on('click', _.before(5, addContactToList)); * // => allows adding up to 4 contacts to the list */ function before(n, func) { var result; if (!isFunction(func)) { if (isFunction(n)) { var temp = n; n = func; func = temp; } else { throw new TypeError(FUNC_ERROR_TEXT); } } return function() { if (--n > 0) { result = func.apply(this, arguments); } else { func = null; } return result; }; } /** * Creates a function that invokes `func` with the `this` binding of `thisArg` * and prepends any additional `_.bind` arguments to those provided to the * bound function. * * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for partially applied arguments. * * **Note:** Unlike native `Function#bind` this method does not set the `length` * property of bound functions. * * @static * @memberOf _ * @category Function * @param {Function} func The function to bind. * @param {*} thisArg The `this` binding of `func`. * @param {...*} [args] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var greet = function(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * }; * * var object = { 'user': 'fred' }; * * var bound = _.bind(greet, object, 'hi'); * bound('!'); * // => 'hi fred!' * * // using placeholders * var bound = _.bind(greet, object, _, '!'); * bound('hi'); * // => 'hi fred!' */ function bind(func, thisArg) { var bitmask = BIND_FLAG; if (arguments.length > 2) { var partials = baseSlice(arguments, 2), holders = replaceHolders(partials, bind.placeholder); bitmask |= PARTIAL_FLAG; } return createWrapper(func, bitmask, thisArg, partials, holders); } /** * Binds methods of an object to the object itself, overwriting the existing * method. Method names may be specified as individual arguments or as arrays * of method names. If no method names are provided all enumerable function * properties, own and inherited, of `object` are bound. * * **Note:** This method does not set the `length` property of bound functions. * * @static * @memberOf _ * @category Function * @param {Object} object The object to bind and assign the bound methods to. * @param {...(string|string[])} [methodNames] The object method names to bind, * specified as individual method names or arrays of method names. * @returns {Object} Returns `object`. * @example * * var view = { * 'label': 'docs', * 'onClick': function() { console.log('clicked ' + this.label); } * }; * * _.bindAll(view); * jQuery('#docs').on('click', view.onClick); * // => logs 'clicked docs' when the element is clicked */ function bindAll(object) { return baseBindAll(object, arguments.length > 1 ? baseFlatten(arguments, false, false, 1) : functions(object) ); } /** * Creates a function that invokes the method at `object[key]` and prepends * any additional `_.bindKey` arguments to those provided to the bound function. * * This method differs from `_.bind` by allowing bound functions to reference * methods that may be redefined or don't yet exist. * See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern) * for more details. * * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * @static * @memberOf _ * @category Function * @param {Object} object The object the method belongs to. * @param {string} key The key of the method. * @param {...*} [args] The arguments to be partially applied. * @returns {Function} Returns the new bound function. * @example * * var object = { * 'user': 'fred', * 'greet': function(greeting, punctuation) { * return greeting + ' ' + this.user + punctuation; * } * }; * * var bound = _.bindKey(object, 'greet', 'hi'); * bound('!'); * // => 'hi fred!' * * object.greet = function(greeting, punctuation) { * return greeting + 'ya ' + this.user + punctuation; * }; * * bound('!'); * // => 'hiya fred!' * * // using placeholders * var bound = _.bindKey(object, 'greet', _, '!'); * bound('hi'); * // => 'hiya fred!' */ function bindKey(object, key) { var bitmask = BIND_FLAG | BIND_KEY_FLAG; if (arguments.length > 2) { var partials = baseSlice(arguments, 2), holders = replaceHolders(partials, bindKey.placeholder); bitmask |= PARTIAL_FLAG; } return createWrapper(key, bitmask, object, partials, holders); } /** * Creates a function that accepts one or more arguments of `func` that when * called either invokes `func` returning its result, if all `func` arguments * have been provided, or returns a function that accepts one or more of the * remaining `func` arguments, and so on. The arity of `func` may be specified * if `func.length` is not sufficient. * * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, * may be used as a placeholder for provided arguments. * * **Note:** This method does not set the `length` property of curried functions. * * @static * @memberOf _ * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curry(abc); * * curried(1)(2)(3); * // => [1, 2, 3] * * curried(1, 2)(3); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // using placeholders * curried(1)(_, 3)(2); * // => [1, 2, 3] */ function curry(func, arity, guard) { if (guard && isIterateeCall(func, arity, guard)) { arity = null; } var result = createWrapper(func, CURRY_FLAG, null, null, null, null, null, arity); result.placeholder = curry.placeholder; return result; } /** * This method is like `_.curry` except that arguments are applied to `func` * in the manner of `_.partialRight` instead of `_.partial`. * * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for provided arguments. * * **Note:** This method does not set the `length` property of curried functions. * * @static * @memberOf _ * @category Function * @param {Function} func The function to curry. * @param {number} [arity=func.length] The arity of `func`. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Function} Returns the new curried function. * @example * * var abc = function(a, b, c) { * return [a, b, c]; * }; * * var curried = _.curryRight(abc); * * curried(3)(2)(1); * // => [1, 2, 3] * * curried(2, 3)(1); * // => [1, 2, 3] * * curried(1, 2, 3); * // => [1, 2, 3] * * // using placeholders * curried(3)(1, _)(2); * // => [1, 2, 3] */ function curryRight(func, arity, guard) { if (guard && isIterateeCall(func, arity, guard)) { arity = null; } var result = createWrapper(func, CURRY_RIGHT_FLAG, null, null, null, null, null, arity); result.placeholder = curryRight.placeholder; return result; } /** * Creates a function that delays invoking `func` until after `wait` milliseconds * have elapsed since the last time it was invoked. The created function comes * with a `cancel` method to cancel delayed invocations. Provide an options * object to indicate that `func` should be invoked on the leading and/or * trailing edge of the `wait` timeout. Subsequent calls to the debounced * function return the result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked * on the trailing edge of the timeout only if the the debounced function is * invoked more than once during the `wait` timeout. * * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) * for details over the differences between `_.debounce` and `_.throttle`. * * @static * @memberOf _ * @category Function * @param {Function} func The function to debounce. * @param {number} wait The number of milliseconds to delay. * @param {Object} [options] The options object. * @param {boolean} [options.leading=false] Specify invoking on the leading * edge of the timeout. * @param {number} [options.maxWait] The maximum time `func` is allowed to be * delayed before it is invoked. * @param {boolean} [options.trailing=true] Specify invoking on the trailing * edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // avoid costly calculations while the window size is in flux * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); * * // invoke `sendMail` when the click event is fired, debouncing subsequent calls * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })); * * // ensure `batchLog` is invoked once after 1 second of debounced calls * var source = new EventSource('/stream'); * jQuery(source).on('message', _.debounce(batchLog, 250, { * 'maxWait': 1000 * })); * * // cancel a debounced call * var todoChanges = _.debounce(batchLog, 1000); * Object.observe(models.todo, todoChanges); * * Object.observe(models, function(changes) { * if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) { * todoChanges.cancel(); * } * }, ['delete']); * * // ...at some point `models.todo` is changed * models.todo.completed = true; * * // ...before 1 second has passed `models.todo` is deleted * // which cancels the debounced `todoChanges` call * delete models.todo; */ function debounce(func, wait, options) { var args, maxTimeoutId, result, stamp, thisArg, timeoutId, trailingCall, lastCalled = 0, maxWait = false, trailing = true; if (!isFunction(func)) { throw new TypeError(FUNC_ERROR_TEXT); } wait = wait < 0 ? 0 : wait; if (options === true) { var leading = true; trailing = false; } else if (isObject(options)) { leading = options.leading; maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait); trailing = 'trailing' in options ? options.trailing : trailing; } function cancel() { if (timeoutId) { clearTimeout(timeoutId); } if (maxTimeoutId) { clearTimeout(maxTimeoutId); } maxTimeoutId = timeoutId = trailingCall = undefined; } function delayed() { var remaining = wait - (now() - stamp); if (remaining <= 0 || remaining > wait) { if (maxTimeoutId) { clearTimeout(maxTimeoutId); } var isCalled = trailingCall; maxTimeoutId = timeoutId = trailingCall = undefined; if (isCalled) { lastCalled = now(); result = func.apply(thisArg, args); if (!timeoutId && !maxTimeoutId) { args = thisArg = null; } } } else { timeoutId = setTimeout(delayed, remaining); } } function maxDelayed() { if (timeoutId) { clearTimeout(timeoutId); } maxTimeoutId = timeoutId = trailingCall = undefined; if (trailing || (maxWait !== wait)) { lastCalled = now(); result = func.apply(thisArg, args); if (!timeoutId && !maxTimeoutId) { args = thisArg = null; } } } function debounced() { args = arguments; stamp = now(); thisArg = this; trailingCall = trailing && (timeoutId || !leading); if (maxWait === false) { var leadingCall = leading && !timeoutId; } else { if (!maxTimeoutId && !leading) { lastCalled = stamp; } var remaining = maxWait - (stamp - lastCalled), isCalled = remaining <= 0 || remaining > maxWait; if (isCalled) { if (maxTimeoutId) { maxTimeoutId = clearTimeout(maxTimeoutId); } lastCalled = stamp; result = func.apply(thisArg, args); } else if (!maxTimeoutId) { maxTimeoutId = setTimeout(maxDelayed, remaining); } } if (isCalled && timeoutId) { timeoutId = clearTimeout(timeoutId); } else if (!timeoutId && wait !== maxWait) { timeoutId = setTimeout(delayed, wait); } if (leadingCall) { isCalled = true; result = func.apply(thisArg, args); } if (isCalled && !timeoutId && !maxTimeoutId) { args = thisArg = null; } return result; } debounced.cancel = cancel; return debounced; } /** * Defers invoking the `func` until the current call stack has cleared. Any * additional arguments are provided to `func` when it is invoked. * * @static * @memberOf _ * @category Function * @param {Function} func The function to defer. * @param {...*} [args] The arguments to invoke the function with. * @returns {number} Returns the timer id. * @example * * _.defer(function(text) { console.log(text); }, 'deferred'); * // logs 'deferred' after one or more milliseconds */ function defer(func) { return baseDelay(func, 1, arguments, 1); } /** * Invokes `func` after `wait` milliseconds. Any additional arguments are * provided to `func` when it is invoked. * * @static * @memberOf _ * @category Function * @param {Function} func The function to delay. * @param {number} wait The number of milliseconds to delay invocation. * @param {...*} [args] The arguments to invoke the function with. * @returns {number} Returns the timer id. * @example * * _.delay(function(text) { console.log(text); }, 1000, 'later'); * // => logs 'later' after one second */ function delay(func, wait) { return baseDelay(func, wait, arguments, 2); } /** * Creates a function that returns the result of invoking the provided * functions with the `this` binding of the created function, where each * successive invocation is supplied the return value of the previous. * * @static * @memberOf _ * @category Function * @param {...Function} [funcs] Functions to invoke. * @returns {Function} Returns the new function. * @example * * function add(x, y) { * return x + y; * } * * function square(n) { * return n * n; * } * * var addSquare = _.flow(add, square); * addSquare(1, 2); * // => 9 */ function flow() { var funcs = arguments, length = funcs.length; if (!length) { return function() {}; } if (!arrayEvery(funcs, isFunction)) { throw new TypeError(FUNC_ERROR_TEXT); } return function() { var index = 0, result = funcs[index].apply(this, arguments); while (++index < length) { result = funcs[index].call(this, result); } return result; }; } /** * This method is like `_.flow` except that it creates a function that * invokes the provided functions from right to left. * * @static * @memberOf _ * @alias backflow, compose * @category Function * @param {...Function} [funcs] Functions to invoke. * @returns {Function} Returns the new function. * @example * * function add(x, y) { * return x + y; * } * * function square(n) { * return n * n; * } * * var addSquare = _.flowRight(square, add); * addSquare(1, 2); * // => 9 */ function flowRight() { var funcs = arguments, fromIndex = funcs.length - 1; if (fromIndex < 0) { return function() {}; } if (!arrayEvery(funcs, isFunction)) { throw new TypeError(FUNC_ERROR_TEXT); } return function() { var index = fromIndex, result = funcs[index].apply(this, arguments); while (index--) { result = funcs[index].call(this, result); } return result; }; } /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided it determines the cache key for storing the result based on the * arguments provided to the memoized function. By default, the first argument * provided to the memoized function is coerced to a string and used as the * cache key. The `func` is invoked with the `this` binding of the memoized * function. * * **Note:** The cache is exposed as the `cache` property on the memoized * function. Its creation may be customized by replacing the `_.memoize.Cache` * constructor with one whose instances implement the ES `Map` method interface * of `get`, `has`, and `set`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-properties-of-the-map-prototype-object) * for more details. * * @static * @memberOf _ * @category Function * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] The function to resolve the cache key. * @returns {Function} Returns the new memoizing function. * @example * * var upperCase = _.memoize(function(string) { * return string.toUpperCase(); * }); * * upperCase('fred'); * // => 'FRED' * * // modifying the result cache * upperCase.cache.set('fred', 'BARNEY'); * upperCase('fred'); * // => 'BARNEY' * * // replacing `_.memoize.Cache` * var object = { 'user': 'fred' }; * var other = { 'user': 'barney' }; * var identity = _.memoize(_.identity); * * identity(object); * // => { 'user': 'fred' } * identity(other); * // => { 'user': 'fred' } * * _.memoize.Cache = WeakMap; * var identity = _.memoize(_.identity); * * identity(object); * // => { 'user': 'fred' } * identity(other); * // => { 'user': 'barney' } */ function memoize(func, resolver) { if (!isFunction(func) || (resolver && !isFunction(resolver))) { throw new TypeError(FUNC_ERROR_TEXT); } var memoized = function() { var cache = memoized.cache, key = resolver ? resolver.apply(this, arguments) : arguments[0]; if (cache.has(key)) { return cache.get(key); } var result = func.apply(this, arguments); cache.set(key, result); return result; }; memoized.cache = new memoize.Cache; return memoized; } /** * Creates a function that negates the result of the predicate `func`. The * `func` predicate is invoked with the `this` binding and arguments of the * created function. * * @static * @memberOf _ * @category Function * @param {Function} predicate The predicate to negate. * @returns {Function} Returns the new function. * @example * * function isEven(n) { * return n % 2 == 0; * } * * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); * // => [1, 3, 5] */ function negate(predicate) { if (!isFunction(predicate)) { throw new TypeError(FUNC_ERROR_TEXT); } return function() { return !predicate.apply(this, arguments); }; } /** * Creates a function that is restricted to invoking `func` once. Repeat calls * to the function return the value of the first call. The `func` is invoked * with the `this` binding of the created function. * * @static * @memberOf _ * @type Function * @category Function * @param {Function} func The function to restrict. * @returns {Function} Returns the new restricted function. * @example * * var initialize = _.once(createApplication); * initialize(); * initialize(); * // `initialize` invokes `createApplication` once */ function once(func) { return before(func, 2); } /** * Creates a function that invokes `func` with `partial` arguments prepended * to those provided to the new function. This method is like `_.bind` except * it does **not** alter the `this` binding. * * The `_.partial.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method does not set the `length` property of partially * applied functions. * * @static * @memberOf _ * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [args] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * var greet = function(greeting, name) { * return greeting + ' ' + name; * }; * * var sayHelloTo = _.partial(greet, 'hello'); * sayHelloTo('fred'); * // => 'hello fred' * * // using placeholders * var greetFred = _.partial(greet, _, 'fred'); * greetFred('hi'); * // => 'hi fred' */ function partial(func) { var partials = baseSlice(arguments, 1), holders = replaceHolders(partials, partial.placeholder); return createWrapper(func, PARTIAL_FLAG, null, partials, holders); } /** * This method is like `_.partial` except that partially applied arguments * are appended to those provided to the new function. * * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic * builds, may be used as a placeholder for partially applied arguments. * * **Note:** This method does not set the `length` property of partially * applied functions. * * @static * @memberOf _ * @category Function * @param {Function} func The function to partially apply arguments to. * @param {...*} [args] The arguments to be partially applied. * @returns {Function} Returns the new partially applied function. * @example * * var greet = function(greeting, name) { * return greeting + ' ' + name; * }; * * var greetFred = _.partialRight(greet, 'fred'); * greetFred('hi'); * // => 'hi fred' * * // using placeholders * var sayHelloTo = _.partialRight(greet, 'hello', _); * sayHelloTo('fred'); * // => 'hello fred' */ function partialRight(func) { var partials = baseSlice(arguments, 1), holders = replaceHolders(partials, partialRight.placeholder); return createWrapper(func, PARTIAL_RIGHT_FLAG, null, partials, holders); } /** * Creates a function that invokes `func` with arguments arranged according * to the specified indexes where the argument value at the first index is * provided as the first argument, the argument value at the second index is * provided as the second argument, and so on. * * @static * @memberOf _ * @category Function * @param {Function} func The function to rearrange arguments for. * @param {...(number|number[])} indexes The arranged argument indexes, * specified as individual indexes or arrays of indexes. * @returns {Function} Returns the new function. * @example * * var rearged = _.rearg(function(a, b, c) { * return [a, b, c]; * }, 2, 0, 1); * * rearged('b', 'c', 'a') * // => ['a', 'b', 'c'] * * var map = _.rearg(_.map, [1, 0]); * map(function(n) { return n * 3; }, [1, 2, 3]); * // => [3, 6, 9] */ function rearg(func) { var indexes = baseFlatten(arguments, false, false, 1); return createWrapper(func, REARG_FLAG, null, null, null, indexes); } /** * Creates a function that only invokes `func` at most once per every `wait` * milliseconds. The created function comes with a `cancel` method to cancel * delayed invocations. Provide an options object to indicate that `func` * should be invoked on the leading and/or trailing edge of the `wait` timeout. * Subsequent calls to the throttled function return the result of the last * `func` call. * * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked * on the trailing edge of the timeout only if the the throttled function is * invoked more than once during the `wait` timeout. * * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation) * for details over the differences between `_.throttle` and `_.debounce`. * * @static * @memberOf _ * @category Function * @param {Function} func The function to throttle. * @param {number} wait The number of milliseconds to throttle invocations to. * @param {Object} [options] The options object. * @param {boolean} [options.leading=true] Specify invoking on the leading * edge of the timeout. * @param {boolean} [options.trailing=true] Specify invoking on the trailing * edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // avoid excessively updating the position while scrolling * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); * * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }) * jQuery('.interactive').on('click', throttled); * * // cancel a trailing throttled call * jQuery(window).on('popstate', throttled.cancel); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (!isFunction(func)) { throw new TypeError(FUNC_ERROR_TEXT); } if (options === false) { leading = false; } else if (isObject(options)) { leading = 'leading' in options ? !!options.leading : leading; trailing = 'trailing' in options ? !!options.trailing : trailing; } debounceOptions.leading = leading; debounceOptions.maxWait = +wait; debounceOptions.trailing = trailing; return debounce(func, wait, debounceOptions); } /** * Creates a function that provides `value` to the wrapper function as its * first argument. Any additional arguments provided to the function are * appended to those provided to the wrapper function. The wrapper is invoked * with the `this` binding of the created function. * * @static * @memberOf _ * @category Function * @param {*} value The value to wrap. * @param {Function} wrapper The wrapper function. * @returns {Function} Returns the new function. * @example * * var p = _.wrap(_.escape, function(func, text) { * return '

' + func(text) + '

'; * }); * * p('fred, barney, & pebbles'); * // => '

fred, barney, & pebbles

' */ function wrap(value, wrapper) { wrapper = wrapper == null ? identity : wrapper; return createWrapper(wrapper, PARTIAL_FLAG, null, [value], []); } /*------------------------------------------------------------------------*/ /** * Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, * otherwise they are assigned by reference. If `customizer` is provided it is * invoked to produce the cloned values. If `customizer` returns `undefined` * cloning is handled by the method instead. The `customizer` is bound to * `thisArg` and invoked with two argument; (value [, index|key, object]). * * **Note:** This method is loosely based on the structured clone algorithm. * The enumerable properties of `arguments` objects and objects created by * constructors other than `Object` are cloned to plain `Object` objects. An * empty object is returned for uncloneable values such as functions, DOM nodes, * Maps, Sets, and WeakMaps. See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm) * for more details. * * @static * @memberOf _ * @category Lang * @param {*} value The value to clone. * @param {boolean} [isDeep] Specify a deep clone. * @param {Function} [customizer] The function to customize cloning values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {*} Returns the cloned value. * @example * * var users = [ * { 'user': 'barney' }, * { 'user': 'fred' } * ]; * * var shallow = _.clone(users); * shallow[0] === users[0]; * // => true * * var deep = _.clone(users, true); * deep[0] === users[0]; * // => false * * // using a customizer callback * var body = _.clone(document.body, function(value) { * return _.isElement(value) ? value.cloneNode(false) : undefined; * }); * * body === document.body * // => false * body.nodeName * // => BODY * body.childNodes.length; * // => 0 */ function clone(value, isDeep, customizer, thisArg) { // Juggle arguments. if (typeof isDeep != 'boolean' && isDeep != null) { thisArg = customizer; customizer = isIterateeCall(value, isDeep, thisArg) ? null : isDeep; isDeep = false; } customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 1); return baseClone(value, isDeep, customizer); } /** * Creates a deep clone of `value`. If `customizer` is provided it is invoked * to produce the cloned values. If `customizer` returns `undefined` cloning * is handled by the method instead. The `customizer` is bound to `thisArg` * and invoked with two argument; (value [, index|key, object]). * * **Note:** This method is loosely based on the structured clone algorithm. * The enumerable properties of `arguments` objects and objects created by * constructors other than `Object` are cloned to plain `Object` objects. An * empty object is returned for uncloneable values such as functions, DOM nodes, * Maps, Sets, and WeakMaps. See the [HTML5 specification](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm) * for more details. * * @static * @memberOf _ * @category Lang * @param {*} value The value to deep clone. * @param {Function} [customizer] The function to customize cloning values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {*} Returns the deep cloned value. * @example * * var users = [ * { 'user': 'barney' }, * { 'user': 'fred' } * ]; * * var deep = _.cloneDeep(users); * deep[0] === users[0]; * // => false * * // using a customizer callback * var el = _.cloneDeep(document.body, function(value) { * return _.isElement(value) ? value.cloneNode(true) : undefined; * }); * * body === document.body * // => false * body.nodeName * // => BODY * body.childNodes.length; * // => 20 */ function cloneDeep(value, customizer, thisArg) { customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 1); return baseClone(value, true, customizer); } /** * Checks if `value` is classified as an `arguments` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * (function() { return _.isArguments(arguments); })(); * // => true * * _.isArguments([1, 2, 3]); * // => false */ function isArguments(value) { var length = isObjectLike(value) ? value.length : undefined; return (isLength(length) && objToString.call(value) == argsTag) || false; } /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * (function() { return _.isArray(arguments); })(); * // => false */ var isArray = nativeIsArray || function(value) { return (isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag) || false; }; /** * Checks if `value` is classified as a boolean primitive or object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isBoolean(false); * // => true * * _.isBoolean(null); * // => false */ function isBoolean(value) { return (value === true || value === false || isObjectLike(value) && objToString.call(value) == boolTag) || false; } /** * Checks if `value` is classified as a `Date` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isDate(new Date); * // => true * * _.isDate('Mon April 23 2012'); * // => false */ function isDate(value) { return (isObjectLike(value) && objToString.call(value) == dateTag) || false; } /** * Checks if `value` is a DOM element. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. * @example * * _.isElement(document.body); * // => true * * _.isElement(''); * // => false */ function isElement(value) { return (value && value.nodeType === 1 && isObjectLike(value) && objToString.call(value).indexOf('Element') > -1) || false; } // Fallback for environments without DOM support. if (!support.dom) { isElement = function(value) { return (value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value)) || false; }; } /** * Checks if a value is empty. A value is considered empty unless it is an * `arguments` object, array, string, or jQuery-like collection with a length * greater than `0` or an object with own enumerable properties. * * @static * @memberOf _ * @category Lang * @param {Array|Object|string} value The value to inspect. * @returns {boolean} Returns `true` if `value` is empty, else `false`. * @example * * _.isEmpty(null); * // => true * * _.isEmpty(true); * // => true * * _.isEmpty(1); * // => true * * _.isEmpty([1, 2, 3]); * // => false * * _.isEmpty({ 'a': 1 }); * // => false */ function isEmpty(value) { if (value == null) { return true; } var length = value.length; if (isLength(length) && (isArray(value) || isString(value) || isArguments(value) || (isObjectLike(value) && isFunction(value.splice)))) { return !length; } return !keys(value).length; } /** * Performs a deep comparison between two values to determine if they are * equivalent. If `customizer` is provided it is invoked to compare values. * If `customizer` returns `undefined` comparisons are handled by the method * instead. The `customizer` is bound to `thisArg` and invoked with three * arguments; (value, other [, index|key]). * * **Note:** This method supports comparing arrays, booleans, `Date` objects, * numbers, `Object` objects, regexes, and strings. Functions and DOM nodes * are **not** supported. Provide a customizer function to extend support * for comparing other values. * * @static * @memberOf _ * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @param {Function} [customizer] The function to customize comparing values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'user': 'fred' }; * var other = { 'user': 'fred' }; * * object == other; * // => false * * _.isEqual(object, other); * // => true * * // using a customizer callback * var array = ['hello', 'goodbye']; * var other = ['hi', 'goodbye']; * * _.isEqual(array, other, function(value, other) { * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; * }); * // => true */ function isEqual(value, other, customizer, thisArg) { customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3); if (!customizer && isStrictComparable(value) && isStrictComparable(other)) { return value === other; } var result = customizer ? customizer(value, other) : undefined; return typeof result == 'undefined' ? baseIsEqual(value, other, customizer) : !!result; } /** * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, * `SyntaxError`, `TypeError`, or `URIError` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an error object, else `false`. * @example * * _.isError(new Error); * // => true * * _.isError(Error); * // => false */ function isError(value) { return (isObjectLike(value) && typeof value.message == 'string' && objToString.call(value) == errorTag) || false; } /** * Checks if `value` is a finite primitive number. * * **Note:** This method is based on ES `Number.isFinite`. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite) * for more details. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. * @example * * _.isFinite(10); * // => true * * _.isFinite('10'); * // => false * * _.isFinite(true); * // => false * * _.isFinite(Object(10)); * // => false * * _.isFinite(Infinity); * // => false */ var isFinite = nativeNumIsFinite || function(value) { return typeof value == 'number' && nativeIsFinite(value); }; /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { // Avoid a Chakra JIT bug in compatibility modes of IE 11. // See https://github.com/jashkenas/underscore/issues/1621 for more details. return typeof value == 'function' || false; } // Fallback for environments that return incorrect `typeof` operator results. if (isFunction(/x/) || (Uint8Array && !isFunction(Uint8Array))) { isFunction = function(value) { // The use of `Object#toString` avoids issues with the `typeof` operator // in older versions of Chrome and Safari which return 'function' for regexes // and Safari 8 equivalents which return 'object' for typed array constructors. return objToString.call(value) == funcTag; }; } /** * Checks if `value` is the language type of `Object`. * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * **Note:** See the [ES5 spec](https://es5.github.io/#x8) for more details. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(1); * // => false */ function isObject(value) { // Avoid a V8 JIT bug in Chrome 19-20. // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. var type = typeof value; return type == 'function' || (value && type == 'object') || false; } /** * Performs a deep comparison between `object` and `source` to determine if * `object` contains equivalent property values. If `customizer` is provided * it is invoked to compare values. If `customizer` returns `undefined` * comparisons are handled by the method instead. The `customizer` is bound * to `thisArg` and invoked with three arguments; (value, other, index|key). * * **Note:** This method supports comparing properties of arrays, booleans, * `Date` objects, numbers, `Object` objects, regexes, and strings. Functions * and DOM nodes are **not** supported. Provide a customizer function to extend * support for comparing other values. * * @static * @memberOf _ * @category Lang * @param {Object} source The object to inspect. * @param {Object} source The object of property values to match. * @param {Function} [customizer] The function to customize comparing values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {boolean} Returns `true` if `object` is a match, else `false`. * @example * * var object = { 'user': 'fred', 'age': 40 }; * * _.isMatch(object, { 'age': 40 }); * // => true * * _.isMatch(object, { 'age': 36 }); * // => false * * // using a customizer callback * var object = { 'greeting': 'hello' }; * var source = { 'greeting': 'hi' }; * * _.isMatch(object, source, function(value, other) { * return _.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/) || undefined; * }); * // => true */ function isMatch(object, source, customizer, thisArg) { var props = keys(source), length = props.length; customizer = typeof customizer == 'function' && bindCallback(customizer, thisArg, 3); if (!customizer && length == 1) { var key = props[0], value = source[key]; if (isStrictComparable(value)) { return object != null && value === object[key] && hasOwnProperty.call(object, key); } } var values = Array(length), strictCompareFlags = Array(length); while (length--) { value = values[length] = source[props[length]]; strictCompareFlags[length] = isStrictComparable(value); } return baseIsMatch(object, props, values, strictCompareFlags, customizer); } /** * Checks if `value` is `NaN`. * * **Note:** This method is not the same as native `isNaN` which returns `true` * for `undefined` and other non-numeric values. See the [ES5 spec](https://es5.github.io/#x15.1.2.4) * for more details. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. * @example * * _.isNaN(NaN); * // => true * * _.isNaN(new Number(NaN)); * // => true * * isNaN(undefined); * // => true * * _.isNaN(undefined); * // => false */ function isNaN(value) { // An `NaN` primitive is the only value that is not equal to itself. // Perform the `toStringTag` check first to avoid errors with some host objects in IE. return isNumber(value) && value != +value; } /** * Checks if `value` is a native function. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, else `false`. * @example * * _.isNative(Array.prototype.push); * // => true * * _.isNative(_); * // => false */ function isNative(value) { if (value == null) { return false; } if (objToString.call(value) == funcTag) { return reNative.test(fnToString.call(value)); } return (isObjectLike(value) && reHostCtor.test(value)) || false; } /** * Checks if `value` is `null`. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `null`, else `false`. * @example * * _.isNull(null); * // => true * * _.isNull(void 0); * // => false */ function isNull(value) { return value === null; } /** * Checks if `value` is classified as a `Number` primitive or object. * * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified * as numbers, use the `_.isFinite` method. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isNumber(8.4); * // => true * * _.isNumber(NaN); * // => true * * _.isNumber('8.4'); * // => false */ function isNumber(value) { return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag) || false; } /** * Checks if `value` is a plain object, that is, an object created by the * `Object` constructor or one with a `[[Prototype]]` of `null`. * * **Note:** This method assumes objects created by the `Object` constructor * have no inherited enumerable properties. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Foo() { * this.a = 1; * } * * _.isPlainObject(new Foo); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true * * _.isPlainObject(Object.create(null)); * // => true */ var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { if (!(value && objToString.call(value) == objectTag)) { return false; } var valueOf = value.valueOf, objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); return objProto ? (value == objProto || getPrototypeOf(value) == objProto) : shimIsPlainObject(value); }; /** * Checks if `value` is classified as a `RegExp` object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isRegExp(/abc/); * // => true * * _.isRegExp('/abc/'); * // => false */ function isRegExp(value) { return (isObjectLike(value) && objToString.call(value) == regexpTag) || false; } /** * Checks if `value` is classified as a `String` primitive or object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isString('abc'); * // => true * * _.isString(1); * // => false */ function isString(value) { return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag) || false; } /** * Checks if `value` is classified as a typed array. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. * @example * * _.isTypedArray(new Uint8Array); * // => true * * _.isTypedArray([]); * // => false */ function isTypedArray(value) { return (isObjectLike(value) && isLength(value.length) && typedArrayTags[objToString.call(value)]) || false; } /** * Checks if `value` is `undefined`. * * @static * @memberOf _ * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. * @example * * _.isUndefined(void 0); * // => true * * _.isUndefined(null); * // => false */ function isUndefined(value) { return typeof value == 'undefined'; } /** * Converts `value` to an array. * * @static * @memberOf _ * @category Lang * @param {*} value The value to convert. * @returns {Array} Returns the converted array. * @example * * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3); * // => [2, 3] */ function toArray(value) { var length = value ? value.length : 0; if (!isLength(length)) { return values(value); } if (!length) { return []; } return arrayCopy(value); } /** * Converts `value` to a plain object flattening inherited enumerable * properties of `value` to own properties of the plain object. * * @static * @memberOf _ * @category Lang * @param {*} value The value to convert. * @returns {Object} Returns the converted plain object. * @example * * function Foo() { * this.b = 2; * } * * Foo.prototype.c = 3; * * _.assign({ 'a': 1 }, new Foo); * // => { 'a': 1, 'b': 2 } * * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); * // => { 'a': 1, 'b': 2, 'c': 3 } */ function toPlainObject(value) { return baseCopy(value, keysIn(value)); } /*------------------------------------------------------------------------*/ /** * Assigns own enumerable properties of source object(s) to the destination * object. Subsequent sources overwrite property assignments of previous sources. * If `customizer` is provided it is invoked to produce the assigned values. * The `customizer` is bound to `thisArg` and invoked with five arguments; * (objectValue, sourceValue, key, object, source). * * @static * @memberOf _ * @alias extend * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @param {Function} [customizer] The function to customize assigning values. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example * * _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); * // => { 'user': 'fred', 'age': 40 } * * // using a customizer callback * var defaults = _.partialRight(_.assign, function(value, other) { * return typeof value == 'undefined' ? other : value; * }); * * defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); * // => { 'user': 'barney', 'age': 36 } */ var assign = createAssigner(baseAssign); /** * Creates an object that inherits from the given `prototype` object. If a * `properties` object is provided its own enumerable properties are assigned * to the created object. * * @static * @memberOf _ * @category Object * @param {Object} prototype The object to inherit from. * @param {Object} [properties] The properties to assign to the object. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Object} Returns the new object. * @example * * function Shape() { * this.x = 0; * this.y = 0; * } * * function Circle() { * Shape.call(this); * } * * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle }); * * var circle = new Circle; * circle instanceof Circle; * // => true * * circle instanceof Shape; * // => true */ function create(prototype, properties, guard) { var result = baseCreate(prototype); if (guard && isIterateeCall(prototype, properties, guard)) { properties = null; } return properties ? baseCopy(properties, result, keys(properties)) : result; } /** * Assigns own enumerable properties of source object(s) to the destination * object for all destination properties that resolve to `undefined`. Once a * property is set, additional defaults of the same property are ignored. * * @static * @memberOf _ * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @returns {Object} Returns `object`. * @example * * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); * // => { 'user': 'barney', 'age': 36 } */ function defaults(object) { if (object == null) { return object; } var args = arrayCopy(arguments); args.push(assignDefaults); return assign.apply(undefined, args); } /** * This method is like `_.findIndex` except that it returns the key of the * first element `predicate` returns truthy for, instead of the element itself. * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {string|undefined} Returns the key of the matched element, else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findKey(users, function(chr) { return chr.age < 40; }); * // => 'barney' (iteration order is not guaranteed) * * // using the "_.matches" callback shorthand * _.findKey(users, { 'age': 1 }); * // => 'pebbles' * * // using the "_.property" callback shorthand * _.findKey(users, 'active'); * // => 'barney' */ function findKey(object, predicate, thisArg) { predicate = getCallback(predicate, thisArg, 3); return baseFind(object, predicate, baseForOwn, true); } /** * This method is like `_.findKey` except that it iterates over elements of * a collection in the opposite order. * * If a property name is provided for `predicate` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `predicate` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to search. * @param {Function|Object|string} [predicate=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {string|undefined} Returns the key of the matched element, else `undefined`. * @example * * var users = { * 'barney': { 'age': 36, 'active': true }, * 'fred': { 'age': 40, 'active': false }, * 'pebbles': { 'age': 1, 'active': true } * }; * * _.findLastKey(users, function(chr) { return chr.age < 40; }); * // => returns `pebbles` assuming `_.findKey` returns `barney` * * // using the "_.matches" callback shorthand * _.findLastKey(users, { 'age': 36 }); * // => 'barney' * * // using the "_.property" callback shorthand * _.findLastKey(users, 'active'); * // => 'pebbles' */ function findLastKey(object, predicate, thisArg) { predicate = getCallback(predicate, thisArg, 3); return baseFind(object, predicate, baseForOwnRight, true); } /** * Iterates over own and inherited enumerable properties of an object invoking * `iteratee` for each property. The `iteratee` is bound to `thisArg` and invoked * with three arguments; (value, key, object). Iterator functions may exit * iteration early by explicitly returning `false`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns `object`. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forIn(new Foo, function(value, key) { * console.log(key); * }); * // => logs 'a', 'b', and 'c' (iteration order is not guaranteed) */ function forIn(object, iteratee, thisArg) { if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { iteratee = bindCallback(iteratee, thisArg, 3); } return baseFor(object, iteratee, keysIn); } /** * This method is like `_.forIn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns `object`. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.forInRight(new Foo, function(value, key) { * console.log(key); * }); * // => logs 'c', 'b', and 'a' assuming `_.forIn ` logs 'a', 'b', and 'c' */ function forInRight(object, iteratee, thisArg) { iteratee = bindCallback(iteratee, thisArg, 3); return baseForRight(object, iteratee, keysIn); } /** * Iterates over own enumerable properties of an object invoking `iteratee` * for each property. The `iteratee` is bound to `thisArg` and invoked with * three arguments; (value, key, object). Iterator functions may exit iteration * early by explicitly returning `false`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns `object`. * @example * * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(n, key) { * console.log(key); * }); * // => logs '0', '1', and 'length' (iteration order is not guaranteed) */ function forOwn(object, iteratee, thisArg) { if (typeof iteratee != 'function' || typeof thisArg != 'undefined') { iteratee = bindCallback(iteratee, thisArg, 3); } return baseForOwn(object, iteratee); } /** * This method is like `_.forOwn` except that it iterates over properties of * `object` in the opposite order. * * @static * @memberOf _ * @category Object * @param {Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns `object`. * @example * * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(n, key) { * console.log(key); * }); * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' */ function forOwnRight(object, iteratee, thisArg) { iteratee = bindCallback(iteratee, thisArg, 3); return baseForRight(object, iteratee, keys); } /** * Creates an array of function property names from all enumerable properties, * own and inherited, of `object`. * * @static * @memberOf _ * @alias methods * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the new array of property names. * @example * * _.functions(_); * // => ['all', 'any', 'bind', ...] */ function functions(object) { return baseFunctions(object, keysIn(object)); } /** * Checks if `key` exists as a direct property of `object` instead of an * inherited property. * * @static * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @param {string} key The key to check. * @returns {boolean} Returns `true` if `key` is a direct property, else `false`. * @example * * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); * // => true */ function has(object, key) { return object ? hasOwnProperty.call(object, key) : false; } /** * Creates an object composed of the inverted keys and values of `object`. * If `object` contains duplicate values, subsequent values overwrite property * assignments of previous values unless `multiValue` is `true`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to invert. * @param {boolean} [multiValue] Allow multiple values per key. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {Object} Returns the new inverted object. * @example * * _.invert({ 'first': 'fred', 'second': 'barney' }); * // => { 'fred': 'first', 'barney': 'second' } * * // without `multiValue` * _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' }); * // => { 'fred': 'third', 'barney': 'second' } * * // with `multiValue` * _.invert({ 'first': 'fred', 'second': 'barney', 'third': 'fred' }, true); * // => { 'fred': ['first', 'third'], 'barney': ['second'] } */ function invert(object, multiValue, guard) { if (guard && isIterateeCall(object, multiValue, guard)) { multiValue = null; } var index = -1, props = keys(object), length = props.length, result = {}; while (++index < length) { var key = props[index], value = object[key]; if (multiValue) { if (hasOwnProperty.call(result, value)) { result[value].push(key); } else { result[value] = [key]; } } else { result[value] = key; } } return result; } /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys) * for more details. * * @static * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ var keys = !nativeKeys ? shimKeys : function(object) { if (object) { var Ctor = object.constructor, length = object.length; } if ((typeof Ctor == 'function' && Ctor.prototype === object) || (typeof object != 'function' && (length && isLength(length)))) { return shimKeys(object); } return isObject(object) ? nativeKeys(object) : []; }; /** * Creates an array of the own and inherited enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keysIn(new Foo); * // => ['a', 'b', 'c'] (iteration order is not guaranteed) */ function keysIn(object) { if (object == null) { return []; } if (!isObject(object)) { object = Object(object); } var length = object.length; length = (length && isLength(length) && (isArray(object) || (support.nonEnumArgs && isArguments(object))) && length) || 0; var Ctor = object.constructor, index = -1, isProto = typeof Ctor == 'function' && Ctor.prototype == object, result = Array(length), skipIndexes = length > 0; while (++index < length) { result[index] = (index + ''); } for (var key in object) { if (!(skipIndexes && isIndex(key, length)) && !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { result.push(key); } } return result; } /** * Creates an object with the same keys as `object` and values generated by * running each own enumerable property of `object` through `iteratee`. The * iteratee function is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * If a property name is provided for `iteratee` the created "_.property" * style callback returns the property value of the given element. * * If an object is provided for `iteratee` the created "_.matches" style * callback returns `true` for elements that have the properties of the given * object, else `false`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to iterate over. * @param {Function|Object|string} [iteratee=_.identity] The function invoked * per iteration. If a property name or object is provided it is used to * create a "_.property" or "_.matches" style callback respectively. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {Object} Returns the new mapped object. * @example * * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(n) { return n * 3; }); * // => { 'a': 3, 'b': 6, 'c': 9 } * * var users = { * 'fred': { 'user': 'fred', 'age': 40 }, * 'pebbles': { 'user': 'pebbles', 'age': 1 } * }; * * // using the "_.property" callback shorthand * _.mapValues(users, 'age'); * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) */ function mapValues(object, iteratee, thisArg) { var result = {}; iteratee = getCallback(iteratee, thisArg, 3); baseForOwn(object, function(value, key, object) { result[key] = iteratee(value, key, object); }); return result; } /** * Recursively merges own enumerable properties of the source object(s), that * don't resolve to `undefined` into the destination object. Subsequent sources * overwrite property assignments of previous sources. If `customizer` is * provided it is invoked to produce the merged values of the destination and * source properties. If `customizer` returns `undefined` merging is handled * by the method instead. The `customizer` is bound to `thisArg` and invoked * with five arguments; (objectValue, sourceValue, key, object, source). * * @static * @memberOf _ * @category Object * @param {Object} object The destination object. * @param {...Object} [sources] The source objects. * @param {Function} [customizer] The function to customize merging properties. * @param {*} [thisArg] The `this` binding of `customizer`. * @returns {Object} Returns `object`. * @example * * var users = { * 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] * }; * * var ages = { * 'data': [{ 'age': 36 }, { 'age': 40 }] * }; * * _.merge(users, ages); * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] } * * // using a customizer callback * var object = { * 'fruits': ['apple'], * 'vegetables': ['beet'] * }; * * var other = { * 'fruits': ['banana'], * 'vegetables': ['carrot'] * }; * * _.merge(object, other, function(a, b) { * return _.isArray(a) ? a.concat(b) : undefined; * }); * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } */ var merge = createAssigner(baseMerge); /** * The opposite of `_.pick`; this method creates an object composed of the * own and inherited enumerable properties of `object` that are not omitted. * Property names may be specified as individual arguments or as arrays of * property names. If `predicate` is provided it is invoked for each property * of `object` omitting the properties `predicate` returns truthy for. The * predicate is bound to `thisArg` and invoked with three arguments; * (value, key, object). * * @static * @memberOf _ * @category Object * @param {Object} object The source object. * @param {Function|...(string|string[])} [predicate] The function invoked per * iteration or property names to omit, specified as individual property * names or arrays of property names. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Object} Returns the new object. * @example * * var object = { 'user': 'fred', 'age': 40 }; * * _.omit(object, 'age'); * // => { 'user': 'fred' } * * _.omit(object, _.isNumber); * // => { 'user': 'fred' } */ function omit(object, predicate, thisArg) { if (object == null) { return {}; } if (typeof predicate != 'function') { var props = arrayMap(baseFlatten(arguments, false, false, 1), String); return pickByArray(object, baseDifference(keysIn(object), props)); } predicate = bindCallback(predicate, thisArg, 3); return pickByCallback(object, function(value, key, object) { return !predicate(value, key, object); }); } /** * Creates a two dimensional array of the key-value pairs for `object`, * e.g. `[[key1, value1], [key2, value2]]`. * * @static * @memberOf _ * @category Object * @param {Object} object The object to inspect. * @returns {Array} Returns the new array of key-value pairs. * @example * * _.pairs({ 'barney': 36, 'fred': 40 }); * // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) */ function pairs(object) { var index = -1, props = keys(object), length = props.length, result = Array(length); while (++index < length) { var key = props[index]; result[index] = [key, object[key]]; } return result; } /** * Creates an object composed of the picked `object` properties. Property * names may be specified as individual arguments or as arrays of property * names. If `predicate` is provided it is invoked for each property of `object` * picking the properties `predicate` returns truthy for. The predicate is * bound to `thisArg` and invoked with three arguments; (value, key, object). * * @static * @memberOf _ * @category Object * @param {Object} object The source object. * @param {Function|...(string|string[])} [predicate] The function invoked per * iteration or property names to pick, specified as individual property * names or arrays of property names. * @param {*} [thisArg] The `this` binding of `predicate`. * @returns {Object} Returns the new object. * @example * * var object = { 'user': 'fred', 'age': 40 }; * * _.pick(object, 'user'); * // => { 'user': 'fred' } * * _.pick(object, _.isString); * // => { 'user': 'fred' } */ function pick(object, predicate, thisArg) { if (object == null) { return {}; } return typeof predicate == 'function' ? pickByCallback(object, bindCallback(predicate, thisArg, 3)) : pickByArray(object, baseFlatten(arguments, false, false, 1)); } /** * Resolves the value of property `key` on `object`. If the value of `key` is * a function it is invoked with the `this` binding of `object` and its result * is returned, else the property value is returned. If the property value is * `undefined` the `defaultValue` is used in its place. * * @static * @memberOf _ * @category Object * @param {Object} object The object to query. * @param {string} key The key of the property to resolve. * @param {*} [defaultValue] The value returned if the property value * resolves to `undefined`. * @returns {*} Returns the resolved value. * @example * * var object = { 'user': 'fred', 'age': _.constant(40) }; * * _.result(object, 'user'); * // => 'fred' * * _.result(object, 'age'); * // => 40 * * _.result(object, 'status', 'busy'); * // => 'busy' * * _.result(object, 'status', _.constant('busy')); * // => 'busy' */ function result(object, key, defaultValue) { var value = object == null ? undefined : object[key]; if (typeof value == 'undefined') { value = defaultValue; } return isFunction(value) ? value.call(object) : value; } /** * An alternative to `_.reduce`; this method transforms `object` to a new * `accumulator` object which is the result of running each of its own enumerable * properties through `iteratee`, with each invocation potentially mutating * the `accumulator` object. The `iteratee` is bound to `thisArg` and invoked * with four arguments; (accumulator, value, key, object). Iterator functions * may exit iteration early by explicitly returning `false`. * * @static * @memberOf _ * @category Object * @param {Array|Object} object The object to iterate over. * @param {Function} [iteratee=_.identity] The function invoked per iteration. * @param {*} [accumulator] The custom accumulator value. * @param {*} [thisArg] The `this` binding of `iteratee`. * @returns {*} Returns the accumulated value. * @example * * var squares = _.transform([1, 2, 3, 4, 5, 6], function(result, n) { * n *= n; * if (n % 2) { * return result.push(n) < 3; * } * }); * // => [1, 9, 25] * * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, n, key) { * result[key] = n * 3; * }); * // => { 'a': 3, 'b': 6, 'c': 9 } */ function transform(object, iteratee, accumulator, thisArg) { var isArr = isArray(object) || isTypedArray(object); iteratee = getCallback(iteratee, thisArg, 4); if (accumulator == null) { if (isArr || isObject(object)) { var Ctor = object.constructor; if (isArr) { accumulator = isArray(object) ? new Ctor : []; } else { accumulator = baseCreate(typeof Ctor == 'function' && Ctor.prototype); } } else { accumulator = {}; } } (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) { return iteratee(accumulator, value, index, object); }); return accumulator; } /** * Creates an array of the own enumerable property values of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.values(new Foo); * // => [1, 2] (iteration order is not guaranteed) * * _.values('hi'); * // => ['h', 'i'] */ function values(object) { return baseValues(object, keys(object)); } /** * Creates an array of the own and inherited enumerable property values * of `object`. * * **Note:** Non-object values are coerced to objects. * * @static * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property values. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.valuesIn(new Foo); * // => [1, 2, 3] (iteration order is not guaranteed) */ function valuesIn(object) { return baseValues(object, keysIn(object)); } /*------------------------------------------------------------------------*/ /** * Produces a random number between `min` and `max` (inclusive). If only one * argument is provided a number between `0` and the given number is returned. * If `floating` is `true`, or either `min` or `max` are floats, a floating-point * number is returned instead of an integer. * * @static * @memberOf _ * @category Number * @param {number} [min=0] The minimum possible value. * @param {number} [max=1] The maximum possible value. * @param {boolean} [floating] Specify returning a floating-point number. * @returns {number} Returns the random number. * @example * * _.random(0, 5); * // => an integer between 0 and 5 * * _.random(5); * // => also an integer between 0 and 5 * * _.random(5, true); * // => a floating-point number between 0 and 5 * * _.random(1.2, 5.2); * // => a floating-point number between 1.2 and 5.2 */ function random(min, max, floating) { if (floating && isIterateeCall(min, max, floating)) { max = floating = null; } var noMin = min == null, noMax = max == null; if (floating == null) { if (noMax && typeof min == 'boolean') { floating = min; min = 1; } else if (typeof max == 'boolean') { floating = max; noMax = true; } } if (noMin && noMax) { max = 1; noMax = false; } min = +min || 0; if (noMax) { max = min; min = 0; } else { max = +max || 0; } if (floating || min % 1 || max % 1) { var rand = nativeRandom(); return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand + '').length - 1)))), max); } return baseRandom(min, max); } /*------------------------------------------------------------------------*/ /** * Converts `string` to camel case. * See [Wikipedia](https://en.wikipedia.org/wiki/CamelCase) for more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the camel cased string. * @example * * _.camelCase('Foo Bar'); * // => 'fooBar' * * _.camelCase('--foo-bar'); * // => 'fooBar' * * _.camelCase('__foo_bar__'); * // => 'fooBar' */ var camelCase = createCompounder(function(result, word, index) { word = word.toLowerCase(); return result + (index ? (word.charAt(0).toUpperCase() + word.slice(1)) : word); }); /** * Capitalizes the first character of `string`. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to capitalize. * @returns {string} Returns the capitalized string. * @example * * _.capitalize('fred'); * // => 'Fred' */ function capitalize(string) { string = baseToString(string); return string && (string.charAt(0).toUpperCase() + string.slice(1)); } /** * Deburrs `string` by converting latin-1 supplementary letters to basic latin letters. * See [Wikipedia](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) * for more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to deburr. * @returns {string} Returns the deburred string. * @example * * _.deburr('déjà vu'); * // => 'deja vu' */ function deburr(string) { string = baseToString(string); return string && string.replace(reLatin1, deburrLetter); } /** * Checks if `string` ends with the given target string. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to search. * @param {string} [target] The string to search for. * @param {number} [position=string.length] The position to search from. * @returns {boolean} Returns `true` if `string` ends with `target`, else `false`. * @example * * _.endsWith('abc', 'c'); * // => true * * _.endsWith('abc', 'b'); * // => false * * _.endsWith('abc', 'b', 2); * // => true */ function endsWith(string, target, position) { string = baseToString(string); target = (target + ''); var length = string.length; position = (typeof position == 'undefined' ? length : nativeMin(position < 0 ? 0 : (+position || 0), length)) - target.length; return position >= 0 && string.indexOf(target, position) == position; } /** * Converts the characters "&", "<", ">", '"', "'", and '`', in `string` to * their corresponding HTML entities. * * **Note:** No other characters are escaped. To escape additional characters * use a third-party library like [_he_](https://mths.be/he). * * Though the ">" character is escaped for symmetry, characters like * ">" and "/" don't require escaping in HTML and have no special meaning * unless they're part of a tag or unquoted attribute value. * See [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) * (under "semi-related fun fact") for more details. * * Backticks are escaped because in Internet Explorer < 9, they can break out * of attribute values or HTML comments. See [#102](https://html5sec.org/#102), * [#108](https://html5sec.org/#108), and [#133](https://html5sec.org/#133) of * the [HTML5 Security Cheatsheet](https://html5sec.org/) for more details. * * When working with HTML you should always quote attribute values to reduce * XSS vectors. See [Ryan Grove's article](http://wonko.com/post/html-escaping) * for more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escape('fred, barney, & pebbles'); * // => 'fred, barney, & pebbles' */ function escape(string) { // Reset `lastIndex` because in IE < 9 `String#replace` does not. string = baseToString(string); return (string && reHasUnescapedHtml.test(string)) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string; } /** * Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*", * "+", "(", ")", "[", "]", "{" and "}" in `string`. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to escape. * @returns {string} Returns the escaped string. * @example * * _.escapeRegExp('[lodash](https://lodash.com/)'); * // => '\[lodash\]\(https://lodash\.com/\)' */ function escapeRegExp(string) { string = baseToString(string); return (string && reHasRegExpChars.test(string)) ? string.replace(reRegExpChars, '\\$&') : string; } /** * Converts `string` to kebab case (a.k.a. spinal case). * See [Wikipedia](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles) for * more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the kebab cased string. * @example * * _.kebabCase('Foo Bar'); * // => 'foo-bar' * * _.kebabCase('fooBar'); * // => 'foo-bar' * * _.kebabCase('__foo_bar__'); * // => 'foo-bar' */ var kebabCase = createCompounder(function(result, word, index) { return result + (index ? '-' : '') + word.toLowerCase(); }); /** * Pads `string` on the left and right sides if it is shorter then the given * padding length. The `chars` string may be truncated if the number of padding * characters can't be evenly divided by the padding length. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.pad('abc', 8); * // => ' abc ' * * _.pad('abc', 8, '_-'); * // => '_-abc_-_' * * _.pad('abc', 3); * // => 'abc' */ function pad(string, length, chars) { string = baseToString(string); length = +length; var strLength = string.length; if (strLength >= length || !nativeIsFinite(length)) { return string; } var mid = (length - strLength) / 2, leftLength = floor(mid), rightLength = ceil(mid); chars = createPad('', rightLength, chars); return chars.slice(0, leftLength) + string + chars; } /** * Pads `string` on the left side if it is shorter then the given padding * length. The `chars` string may be truncated if the number of padding * characters exceeds the padding length. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padLeft('abc', 6); * // => ' abc' * * _.padLeft('abc', 6, '_-'); * // => '_-_abc' * * _.padLeft('abc', 3); * // => 'abc' */ function padLeft(string, length, chars) { string = baseToString(string); return string && (createPad(string, length, chars) + string); } /** * Pads `string` on the right side if it is shorter then the given padding * length. The `chars` string may be truncated if the number of padding * characters exceeds the padding length. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to pad. * @param {number} [length=0] The padding length. * @param {string} [chars=' '] The string used as padding. * @returns {string} Returns the padded string. * @example * * _.padRight('abc', 6); * // => 'abc ' * * _.padRight('abc', 6, '_-'); * // => 'abc_-_' * * _.padRight('abc', 3); * // => 'abc' */ function padRight(string, length, chars) { string = baseToString(string); return string && (string + createPad(string, length, chars)); } /** * Converts `string` to an integer of the specified radix. If `radix` is * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal, * in which case a `radix` of `16` is used. * * **Note:** This method aligns with the ES5 implementation of `parseInt`. * See the [ES5 spec](https://es5.github.io/#E) for more details. * * @static * @memberOf _ * @category String * @param {string} string The string to convert. * @param {number} [radix] The radix to interpret `value` by. * @param- {Object} [guard] Enables use as a callback for functions like `_.map`. * @returns {number} Returns the converted integer. * @example * * _.parseInt('08'); * // => 8 * * _.map(['6', '08', '10'], _.parseInt); * // => [6, 8, 10] */ function parseInt(string, radix, guard) { if (guard && isIterateeCall(string, radix, guard)) { radix = 0; } return nativeParseInt(string, radix); } // Fallback for environments with pre-ES5 implementations. if (nativeParseInt(whitespace + '08') != 8) { parseInt = function(string, radix, guard) { // Firefox < 21 and Opera < 15 follow ES3 for `parseInt`. // Chrome fails to trim leading whitespace characters. // See https://code.google.com/p/v8/issues/detail?id=3109 for more details. if (guard ? isIterateeCall(string, radix, guard) : radix == null) { radix = 0; } else if (radix) { radix = +radix; } string = trim(string); return nativeParseInt(string, radix || (reHexPrefix.test(string) ? 16 : 10)); }; } /** * Repeats the given string `n` times. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to repeat. * @param {number} [n=0] The number of times to repeat the string. * @returns {string} Returns the repeated string. * @example * * _.repeat('*', 3); * // => '***' * * _.repeat('abc', 2); * // => 'abcabc' * * _.repeat('abc', 0); * // => '' */ function repeat(string, n) { var result = ''; string = baseToString(string); n = +n; if (n < 1 || !string || !nativeIsFinite(n)) { return result; } // Leverage the exponentiation by squaring algorithm for a faster repeat. // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. do { if (n % 2) { result += string; } n = floor(n / 2); string += string; } while (n); return result; } /** * Converts `string` to snake case. * See [Wikipedia](https://en.wikipedia.org/wiki/Snake_case) for more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the snake cased string. * @example * * _.snakeCase('Foo Bar'); * // => 'foo_bar' * * _.snakeCase('fooBar'); * // => 'foo_bar' * * _.snakeCase('--foo-bar'); * // => 'foo_bar' */ var snakeCase = createCompounder(function(result, word, index) { return result + (index ? '_' : '') + word.toLowerCase(); }); /** * Converts `string` to start case. * See [Wikipedia](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage) * for more details. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the start cased string. * @example * * _.startCase('--foo-bar'); * // => 'Foo Bar' * * _.startCase('fooBar'); * // => 'Foo Bar' * * _.startCase('__foo_bar__'); * // => 'Foo Bar' */ var startCase = createCompounder(function(result, word, index) { return result + (index ? ' ' : '') + (word.charAt(0).toUpperCase() + word.slice(1)); }); /** * Checks if `string` starts with the given target string. * * @static * @memberOf _ * @category String * @param {string} [string=''] The string to search. * @param {string} [target] The string to search for. * @param {number} [position=0] The position to search from. * @returns {boolean} Returns `true` if `string` starts with `target`, else `false`. * @example * * _.startsWith('abc', 'a'); * // => true * * _.startsWith('abc', 'b'); * // => false * * _.startsWith('abc', 'b', 1); * // => true */ function startsWith(string, target, position) { string = baseToString(string); position = position == null ? 0 : nativeMin(position < 0 ? 0 : (+position || 0), string.length); return string.lastIndexOf(target, position) == position; } /** * Creates a compiled template function that can interpolate data properties * in "interpolate" delimiters, HTML-escape interpolated data properties in * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data * properties may be accessed as free variables in the template. If a setting * object is provided it takes precedence over `_.templateSettings` values. * * **Note:** In the development build `_.template` utilizes sourceURLs for easier debugging. * See the [HTML5 Rocks article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) * for more details. * * For more information on precompiling templates see * [lodash's custom builds documentation](https://lodash.com/custom-builds). * * For more information on Chrome extension sandboxes see * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). * * @static * @memberOf _ * @category String * @param {string} [string=''] The template string. * @param {Object} [options] The options object. * @param {RegExp} [options.escape] The HTML "escape" delimiter. * @param {RegExp} [options.evaluate] The "evaluate" delimiter. * @param {Object} [options.imports] An object to import into the template as free variables. * @param {RegExp} [options.interpolate] The "interpolate" delimiter. * @param {string} [options.sourceURL] The sourceURL of the template's compiled source. * @param {string} [options.variable] The data object variable name. * @param- {Object} [otherOptions] Enables the legacy `options` param signature. * @returns {Function} Returns the compiled template function. * @example * * // using the "interpolate" delimiter to create a compiled template * var compiled = _.template('hello <%= user %>!'); * compiled({ 'user': 'fred' }); * // => 'hello fred!' * * // using the HTML "escape" delimiter to escape data property values * var compiled = _.template('<%- value %>'); * compiled({ 'value': '