|  | /** | 
|  | * Canteen v1.0.4 | 
|  | * August 19th, 2015 | 
|  | * | 
|  | * Copyright 2015 Platfora, Inc. | 
|  | * | 
|  | * 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. | 
|  | */ | 
|  | ;(function() { | 
|  | // ================================ Constants ================================ | 
|  | var CONTEXT_2D_ATTRIBUTES = [ | 
|  | 'fillStyle', | 
|  | 'font', | 
|  | 'globalAlpha', | 
|  | 'globalCompositeOperation', | 
|  | 'lineCap', | 
|  | 'lineDashOffset', | 
|  | 'lineJoin', | 
|  | 'lineWidth', | 
|  | 'miterLimit', | 
|  | 'shadowBlur', | 
|  | 'shadowColor', | 
|  | 'shadowOffsetX', | 
|  | 'shadowOffsetY', | 
|  | 'strokeStyle', | 
|  | 'textAlign', | 
|  | 'textBaseline' | 
|  | ]; | 
|  |  | 
|  | // ================================ Utils ================================ | 
|  |  | 
|  | function each(arr, func) { | 
|  | var len = arr.length, | 
|  | n; | 
|  |  | 
|  | for (n=0; n<len; n++) { | 
|  | func(arr[n], n); | 
|  | } | 
|  | } | 
|  |  | 
|  | function round(val, decimalPoints) { | 
|  | var power = Math.pow(10, decimalPoints); | 
|  | return Math.round(val * power) / power; | 
|  | } | 
|  |  | 
|  | function roundArr(arr, decimalPoints) { | 
|  | var len = arr.length, | 
|  | ret = [], | 
|  | n; | 
|  |  | 
|  | for (n=0; n<len; n++) { | 
|  | if (isNumber(arr[n])) { | 
|  | ret.push(round(arr[n], decimalPoints)); | 
|  | } | 
|  | else { | 
|  | ret.push(arr[n]); | 
|  | } | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | function isFunction(func) { | 
|  | return func && {}.toString.call(func) === '[object Function]'; | 
|  | } | 
|  |  | 
|  | function isNumber(val) { | 
|  | return typeof val === 'number'; | 
|  | } | 
|  |  | 
|  | // ================================ Canteen Class ================================ | 
|  |  | 
|  | /** | 
|  | * Canteen Constructor | 
|  | * @constructor | 
|  | */ | 
|  | var Canteen = function(context) { | 
|  | var that = this; | 
|  |  | 
|  | this._stack = []; | 
|  | this.context = context; | 
|  |  | 
|  | // add observable attributes | 
|  | each(CONTEXT_2D_ATTRIBUTES, function(key, n) { | 
|  | Object.defineProperty(that, key, { | 
|  | get: function() { | 
|  | return that.context[key]; | 
|  | }, | 
|  | set: function(val) { | 
|  | that._pushAttr(key, val); | 
|  | that.context[key] = val; | 
|  | } | 
|  | }); | 
|  | }); | 
|  | }; | 
|  |  | 
|  | // Canteen methods | 
|  | Canteen.prototype = { | 
|  | /** | 
|  | * get a stack of operations | 
|  | * @method stack | 
|  | * @param {Object} config | 
|  | * @param {String} [config.loose=false] - strict mode returns method calls with arguments and property names | 
|  | *  with values.  loose mode only returns method calls and property names | 
|  | * @param {Number} [config.decimalPoints=3] - number of decimal points to round numeric values to.  The default is | 
|  | *  3, i.e. 1.23456 will round to 1.234 | 
|  | * @returns {Array} | 
|  | * @public | 
|  | */ | 
|  | stack: function(config) { | 
|  | var config = config || {}, | 
|  | loose = config.loose, | 
|  | decimalPoints = config.decimalPoints === undefined ? 3 : config.decimalPoints, | 
|  | ret = []; | 
|  |  | 
|  | if (loose) { | 
|  | each(this._stack, function(el, n) { | 
|  | ret.push(el.method || el.attr); | 
|  | }); | 
|  | } | 
|  | else { | 
|  | each(this._stack, function(el, n) { | 
|  | // if method instruction | 
|  | if (el.method) { | 
|  | ret.push({ | 
|  | method: el.method, | 
|  | arguments: roundArr(el.arguments, decimalPoints) | 
|  | }); | 
|  | } | 
|  | // if attr | 
|  | else if (el.attr) { | 
|  | ret.push({ | 
|  | attr: el.attr, | 
|  | val: isNumber(el.val) ? round(el.val, decimalPoints) : el.val | 
|  | }); | 
|  | } | 
|  | }); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | }, | 
|  | /** | 
|  | * serialize a stack into a string | 
|  | * @method json | 
|  | * @param {Object} config | 
|  | * @param {String} [config.loose=false] - strict mode returns method calls with arguments and property names | 
|  | *  with values.  loose mode only returns method calls and property names | 
|  | * @param {Number} [config.decimalPoints=3] - number of decimal points to round numeric values to.  The default is | 
|  | *  3, i.e. 1.23456 will round to 1.234 | 
|  | * @returns {String} | 
|  | * @public | 
|  | */ | 
|  | json: function(config) { | 
|  | return JSON.stringify(this.stack(config)); | 
|  | }, | 
|  | /** | 
|  | * convert a stack into a small hash string for easy comparisons | 
|  | * @method hash | 
|  | * @param {Object} config | 
|  | * @param {String} [config.loose=false] - strict mode returns method calls with arguments and property names | 
|  | *  with values.  loose mode only returns method calls and property names | 
|  | * @param {Number} [config.decimalPoints=3] - number of decimal points to round numeric values to.  The default is | 
|  | *  3, i.e. 1.23456 will round to 1.234 | 
|  | * @public | 
|  | * @returns {String} | 
|  | */ | 
|  | hash: function(config) { | 
|  | return Canteen.md5(this.json(config)); | 
|  | }, | 
|  | /** | 
|  | * clear the stack | 
|  | * @method clear | 
|  | * @public | 
|  | */ | 
|  | clear: function() { | 
|  | this._stack = []; | 
|  | }, | 
|  | /** | 
|  | * push instruction method onto the stack | 
|  | * @method _pushMethod | 
|  | * @param {String} method | 
|  | * @param {arguments} args | 
|  | * @private | 
|  | */ | 
|  | _pushMethod: function(method, args) { | 
|  | this._stack.push({ | 
|  | method: method, | 
|  | arguments: Array.prototype.slice.call(args, 0) | 
|  | }); | 
|  |  | 
|  | this._slice(); | 
|  | }, | 
|  | /** | 
|  | * push instruction attribute onto the stack | 
|  | * @method _pushAttr | 
|  | * @param {String} attr | 
|  | * @param {*} val | 
|  | * @private | 
|  | */ | 
|  | _pushAttr: function(attr, val) { | 
|  | this._stack.push({ | 
|  | attr: attr, | 
|  | val: val | 
|  | }); | 
|  |  | 
|  | this._slice(); | 
|  | }, | 
|  | /** | 
|  | * slice the stack if needed.  This means making sure that it doesn't exceed | 
|  | *  the STACK_SIZE.  if it does, then shorten the stack starting from the beginning | 
|  | * @method _slice | 
|  | * @private | 
|  | */ | 
|  | _slice: function() { | 
|  | var stack = this._stack, | 
|  | len = stack.length, | 
|  | exceded = len - Canteen.globals.STACK_SIZE; | 
|  | if (exceded > 0) { | 
|  | this._stack = stack.slice(exceded); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // generate observable methods and add them to the Canteen prototype | 
|  | (function(){ | 
|  | var proto = CanvasRenderingContext2D.prototype, | 
|  | key, val, desc; | 
|  |  | 
|  | function addMethod(key, val) { | 
|  | Canteen.prototype[key] = function() { | 
|  | this._pushMethod(key, arguments); | 
|  | return this.context[key].apply(this.context, arguments); | 
|  | }; | 
|  | } | 
|  |  | 
|  | for (key in proto) { | 
|  | desc = Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, key); | 
|  | val = (desc && desc.value ? proto[key] : null); | 
|  | if (isFunction(val)) { | 
|  | addMethod(key, val); | 
|  | } | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // ================================ Global Config ================================ | 
|  | /** | 
|  | * global config.  You can directly change these values in order to configure Canteen | 
|  | * @static | 
|  | * @example | 
|  | *  // change stack size to 3000 | 
|  | *  Canteen.globals.STACK_SIZE = 3000; | 
|  | */ | 
|  | Canteen.globals = { | 
|  | STACK_SIZE: 10000 | 
|  | }; | 
|  |  | 
|  | // ================================ Initialization ================================ | 
|  |  | 
|  | // override the canvas context getContext method in order to automatically instantiate | 
|  | // a Canteen instance and wrap the native context object | 
|  | (function(){ | 
|  | var origGetContext = HTMLCanvasElement.prototype.getContext; | 
|  |  | 
|  | HTMLCanvasElement.prototype.getContext = function() { | 
|  | var context = origGetContext.apply(this, arguments); | 
|  |  | 
|  | // if the context already has a canteen instance, then return it | 
|  | if (context.canteen) { | 
|  | return context.canteen | 
|  | } | 
|  | // if the context does not have a canteen instance, then instantiate one | 
|  | // and return it | 
|  | else { | 
|  | context.canteen = new Canteen(context); | 
|  | return context.canteen; | 
|  | } | 
|  | } | 
|  | })(); | 
|  |  | 
|  | // make the Canteen namespace global so that developers can configure | 
|  | // it via Canteen.globals, or override methods if desired | 
|  | window.Canteen = Canteen; | 
|  | })(); | 
|  | ;/* | 
|  | * JavaScript MD5 1.0.1 | 
|  | * https://github.com/blueimp/JavaScript-MD5 | 
|  | * | 
|  | * Copyright 2011, Sebastian Tschan | 
|  | * https://blueimp.net | 
|  | * | 
|  | * Licensed under the MIT license: | 
|  | * http://www.opensource.org/licenses/MIT | 
|  | * | 
|  | * Based on | 
|  | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message | 
|  | * Digest Algorithm, as defined in RFC 1321. | 
|  | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 | 
|  | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet | 
|  | * Distributed under the BSD License | 
|  | * See http://pajhome.org.uk/crypt/md5 for more info. | 
|  | */ | 
|  |  | 
|  | /*jslint bitwise: true */ | 
|  | /*global unescape, define */ | 
|  | (function ($) { | 
|  | 'use strict'; | 
|  |  | 
|  | /* | 
|  | * Add integers, wrapping at 2^32. This uses 16-bit operations internally | 
|  | * to work around bugs in some JS interpreters. | 
|  | */ | 
|  | function safe_add(x, y) { | 
|  | var lsw = (x & 0xFFFF) + (y & 0xFFFF), | 
|  | msw = (x >> 16) + (y >> 16) + (lsw >> 16); | 
|  | return (msw << 16) | (lsw & 0xFFFF); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Bitwise rotate a 32-bit number to the left. | 
|  | */ | 
|  | function bit_rol(num, cnt) { | 
|  | return (num << cnt) | (num >>> (32 - cnt)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * These functions implement the four basic operations the algorithm uses. | 
|  | */ | 
|  | function md5_cmn(q, a, b, x, s, t) { | 
|  | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); | 
|  | } | 
|  | function md5_ff(a, b, c, d, x, s, t) { | 
|  | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); | 
|  | } | 
|  | function md5_gg(a, b, c, d, x, s, t) { | 
|  | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); | 
|  | } | 
|  | function md5_hh(a, b, c, d, x, s, t) { | 
|  | return md5_cmn(b ^ c ^ d, a, b, x, s, t); | 
|  | } | 
|  | function md5_ii(a, b, c, d, x, s, t) { | 
|  | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate the MD5 of an array of little-endian words, and a bit length. | 
|  | */ | 
|  | function binl_md5(x, len) { | 
|  | /* append padding */ | 
|  | x[len >> 5] |= 0x80 << (len % 32); | 
|  | x[(((len + 64) >>> 9) << 4) + 14] = len; | 
|  |  | 
|  | var i, olda, oldb, oldc, oldd, | 
|  | a =  1732584193, | 
|  | b = -271733879, | 
|  | c = -1732584194, | 
|  | d =  271733878; | 
|  |  | 
|  | for (i = 0; i < x.length; i += 16) { | 
|  | olda = a; | 
|  | oldb = b; | 
|  | oldc = c; | 
|  | oldd = d; | 
|  |  | 
|  | a = md5_ff(a, b, c, d, x[i],       7, -680876936); | 
|  | d = md5_ff(d, a, b, c, x[i +  1], 12, -389564586); | 
|  | c = md5_ff(c, d, a, b, x[i +  2], 17,  606105819); | 
|  | b = md5_ff(b, c, d, a, x[i +  3], 22, -1044525330); | 
|  | a = md5_ff(a, b, c, d, x[i +  4],  7, -176418897); | 
|  | d = md5_ff(d, a, b, c, x[i +  5], 12,  1200080426); | 
|  | c = md5_ff(c, d, a, b, x[i +  6], 17, -1473231341); | 
|  | b = md5_ff(b, c, d, a, x[i +  7], 22, -45705983); | 
|  | a = md5_ff(a, b, c, d, x[i +  8],  7,  1770035416); | 
|  | d = md5_ff(d, a, b, c, x[i +  9], 12, -1958414417); | 
|  | c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); | 
|  | b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); | 
|  | a = md5_ff(a, b, c, d, x[i + 12],  7,  1804603682); | 
|  | d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); | 
|  | c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); | 
|  | b = md5_ff(b, c, d, a, x[i + 15], 22,  1236535329); | 
|  |  | 
|  | a = md5_gg(a, b, c, d, x[i +  1],  5, -165796510); | 
|  | d = md5_gg(d, a, b, c, x[i +  6],  9, -1069501632); | 
|  | c = md5_gg(c, d, a, b, x[i + 11], 14,  643717713); | 
|  | b = md5_gg(b, c, d, a, x[i],      20, -373897302); | 
|  | a = md5_gg(a, b, c, d, x[i +  5],  5, -701558691); | 
|  | d = md5_gg(d, a, b, c, x[i + 10],  9,  38016083); | 
|  | c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); | 
|  | b = md5_gg(b, c, d, a, x[i +  4], 20, -405537848); | 
|  | a = md5_gg(a, b, c, d, x[i +  9],  5,  568446438); | 
|  | d = md5_gg(d, a, b, c, x[i + 14],  9, -1019803690); | 
|  | c = md5_gg(c, d, a, b, x[i +  3], 14, -187363961); | 
|  | b = md5_gg(b, c, d, a, x[i +  8], 20,  1163531501); | 
|  | a = md5_gg(a, b, c, d, x[i + 13],  5, -1444681467); | 
|  | d = md5_gg(d, a, b, c, x[i +  2],  9, -51403784); | 
|  | c = md5_gg(c, d, a, b, x[i +  7], 14,  1735328473); | 
|  | b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); | 
|  |  | 
|  | a = md5_hh(a, b, c, d, x[i +  5],  4, -378558); | 
|  | d = md5_hh(d, a, b, c, x[i +  8], 11, -2022574463); | 
|  | c = md5_hh(c, d, a, b, x[i + 11], 16,  1839030562); | 
|  | b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); | 
|  | a = md5_hh(a, b, c, d, x[i +  1],  4, -1530992060); | 
|  | d = md5_hh(d, a, b, c, x[i +  4], 11,  1272893353); | 
|  | c = md5_hh(c, d, a, b, x[i +  7], 16, -155497632); | 
|  | b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); | 
|  | a = md5_hh(a, b, c, d, x[i + 13],  4,  681279174); | 
|  | d = md5_hh(d, a, b, c, x[i],      11, -358537222); | 
|  | c = md5_hh(c, d, a, b, x[i +  3], 16, -722521979); | 
|  | b = md5_hh(b, c, d, a, x[i +  6], 23,  76029189); | 
|  | a = md5_hh(a, b, c, d, x[i +  9],  4, -640364487); | 
|  | d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); | 
|  | c = md5_hh(c, d, a, b, x[i + 15], 16,  530742520); | 
|  | b = md5_hh(b, c, d, a, x[i +  2], 23, -995338651); | 
|  |  | 
|  | a = md5_ii(a, b, c, d, x[i],       6, -198630844); | 
|  | d = md5_ii(d, a, b, c, x[i +  7], 10,  1126891415); | 
|  | c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); | 
|  | b = md5_ii(b, c, d, a, x[i +  5], 21, -57434055); | 
|  | a = md5_ii(a, b, c, d, x[i + 12],  6,  1700485571); | 
|  | d = md5_ii(d, a, b, c, x[i +  3], 10, -1894986606); | 
|  | c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); | 
|  | b = md5_ii(b, c, d, a, x[i +  1], 21, -2054922799); | 
|  | a = md5_ii(a, b, c, d, x[i +  8],  6,  1873313359); | 
|  | d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); | 
|  | c = md5_ii(c, d, a, b, x[i +  6], 15, -1560198380); | 
|  | b = md5_ii(b, c, d, a, x[i + 13], 21,  1309151649); | 
|  | a = md5_ii(a, b, c, d, x[i +  4],  6, -145523070); | 
|  | d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); | 
|  | c = md5_ii(c, d, a, b, x[i +  2], 15,  718787259); | 
|  | b = md5_ii(b, c, d, a, x[i +  9], 21, -343485551); | 
|  |  | 
|  | a = safe_add(a, olda); | 
|  | b = safe_add(b, oldb); | 
|  | c = safe_add(c, oldc); | 
|  | d = safe_add(d, oldd); | 
|  | } | 
|  | return [a, b, c, d]; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert an array of little-endian words to a string | 
|  | */ | 
|  | function binl2rstr(input) { | 
|  | var i, | 
|  | output = ''; | 
|  | for (i = 0; i < input.length * 32; i += 8) { | 
|  | output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); | 
|  | } | 
|  | return output; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert a raw string to an array of little-endian words | 
|  | * Characters >255 have their high-byte silently ignored. | 
|  | */ | 
|  | function rstr2binl(input) { | 
|  | var i, | 
|  | output = []; | 
|  | output[(input.length >> 2) - 1] = undefined; | 
|  | for (i = 0; i < output.length; i += 1) { | 
|  | output[i] = 0; | 
|  | } | 
|  | for (i = 0; i < input.length * 8; i += 8) { | 
|  | output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32); | 
|  | } | 
|  | return output; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate the MD5 of a raw string | 
|  | */ | 
|  | function rstr_md5(s) { | 
|  | return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Calculate the HMAC-MD5, of a key and some data (raw strings) | 
|  | */ | 
|  | function rstr_hmac_md5(key, data) { | 
|  | var i, | 
|  | bkey = rstr2binl(key), | 
|  | ipad = [], | 
|  | opad = [], | 
|  | hash; | 
|  | ipad[15] = opad[15] = undefined; | 
|  | if (bkey.length > 16) { | 
|  | bkey = binl_md5(bkey, key.length * 8); | 
|  | } | 
|  | for (i = 0; i < 16; i += 1) { | 
|  | ipad[i] = bkey[i] ^ 0x36363636; | 
|  | opad[i] = bkey[i] ^ 0x5C5C5C5C; | 
|  | } | 
|  | hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); | 
|  | return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert a raw string to a hex string | 
|  | */ | 
|  | function rstr2hex(input) { | 
|  | var hex_tab = '0123456789abcdef', | 
|  | output = '', | 
|  | x, | 
|  | i; | 
|  | for (i = 0; i < input.length; i += 1) { | 
|  | x = input.charCodeAt(i); | 
|  | output += hex_tab.charAt((x >>> 4) & 0x0F) + | 
|  | hex_tab.charAt(x & 0x0F); | 
|  | } | 
|  | return output; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Encode a string as utf-8 | 
|  | */ | 
|  | function str2rstr_utf8(input) { | 
|  | return unescape(encodeURIComponent(input)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Take string arguments and return either raw or hex encoded strings | 
|  | */ | 
|  | function raw_md5(s) { | 
|  | return rstr_md5(str2rstr_utf8(s)); | 
|  | } | 
|  | function hex_md5(s) { | 
|  | return rstr2hex(raw_md5(s)); | 
|  | } | 
|  | function raw_hmac_md5(k, d) { | 
|  | return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)); | 
|  | } | 
|  | function hex_hmac_md5(k, d) { | 
|  | return rstr2hex(raw_hmac_md5(k, d)); | 
|  | } | 
|  |  | 
|  | function md5(string, key, raw) { | 
|  | if (!key) { | 
|  | if (!raw) { | 
|  | return hex_md5(string); | 
|  | } | 
|  | return raw_md5(string); | 
|  | } | 
|  | if (!raw) { | 
|  | return hex_hmac_md5(key, string); | 
|  | } | 
|  | return raw_hmac_md5(key, string); | 
|  | } | 
|  |  | 
|  | if (typeof define === 'function' && define.amd) { | 
|  | define(function () { | 
|  | return md5; | 
|  | }); | 
|  | } else { | 
|  | $.md5 = md5; | 
|  | } | 
|  | }(Canteen)); |