(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global['y-tests'] = global['y-tests'] || {}))); }(this, (function (exports) { 'use strict'; var index$1 = function (str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase(); }); }; /* object-assign (c) Sindre Sorhus @license MIT */ /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] === '5') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test2 = {}; for (var i = 0; i < 10; i++) { test2['_' + String.fromCharCode(i)] = i; } var order2 = Object.getOwnPropertyNames(test2).map(function (n) { return test2[n]; }); if (order2.join('') !== '0123456789') { return false; } // https://bugs.chromium.org/p/v8/issues/detail?id=3056 var test3 = {}; 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { test3[letter] = letter; }); if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') { return false; } return true; } catch (err) { // We don't expect any of the above to throw, but better to be safe. return false; } } var index$3 = shouldUseNative() ? Object.assign : function (target, source) { var from; var to = toObject(target); var symbols; for (var s = 1; s < arguments.length; s++) { from = Object(arguments[s]); for (var key in from) { if (hasOwnProperty.call(from, key)) { to[key] = from[key]; } } if (getOwnPropertySymbols) { symbols = getOwnPropertySymbols(from); for (var i = 0; i < symbols.length; i++) { if (propIsEnumerable.call(from, symbols[i])) { to[symbols[i]] = from[symbols[i]]; } } } } return to; }; function encoderForArrayFormat(opts) { switch (opts.arrayFormat) { case 'index': return function (key, value, index) { return value === null ? [ encode(key, opts), '[', index, ']' ].join('') : [ encode(key, opts), '[', encode(index, opts), ']=', encode(value, opts) ].join(''); }; case 'bracket': return function (key, value) { return value === null ? encode(key, opts) : [ encode(key, opts), '[]=', encode(value, opts) ].join(''); }; default: return function (key, value) { return value === null ? encode(key, opts) : [ encode(key, opts), '=', encode(value, opts) ].join(''); }; } } function parserForArrayFormat(opts) { var result; switch (opts.arrayFormat) { case 'index': return function (key, value, accumulator) { result = /\[(\d*)\]$/.exec(key); key = key.replace(/\[\d*\]$/, ''); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === undefined) { accumulator[key] = {}; } accumulator[key][result[1]] = value; }; case 'bracket': return function (key, value, accumulator) { result = /(\[\])$/.exec(key); key = key.replace(/\[\]$/, ''); if (!result) { accumulator[key] = value; return; } else if (accumulator[key] === undefined) { accumulator[key] = [value]; return; } accumulator[key] = [].concat(accumulator[key], value); }; default: return function (key, value, accumulator) { if (accumulator[key] === undefined) { accumulator[key] = value; return; } accumulator[key] = [].concat(accumulator[key], value); }; } } function encode(value, opts) { if (opts.encode) { return opts.strict ? index$1(value) : encodeURIComponent(value); } return value; } function keysSorter(input) { if (Array.isArray(input)) { return input.sort(); } else if (typeof input === 'object') { return keysSorter(Object.keys(input)).sort(function (a, b) { return Number(a) - Number(b); }).map(function (key) { return input[key]; }); } return input; } var extract = function (str) { return str.split('?')[1] || ''; }; var parse = function (str, opts) { opts = index$3({arrayFormat: 'none'}, opts); var formatter = parserForArrayFormat(opts); // Create an object with no prototype // https://github.com/sindresorhus/query-string/issues/47 var ret = Object.create(null); if (typeof str !== 'string') { return ret; } str = str.trim().replace(/^(\?|#|&)/, ''); if (!str) { return ret; } str.split('&').forEach(function (param) { var parts = param.replace(/\+/g, ' ').split('='); // Firefox (pre 40) decodes `%3D` to `=` // https://github.com/sindresorhus/query-string/pull/37 var key = parts.shift(); var val = parts.length > 0 ? parts.join('=') : undefined; // missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters val = val === undefined ? null : decodeURIComponent(val); formatter(decodeURIComponent(key), val, ret); }); return Object.keys(ret).sort().reduce(function (result, key) { var val = ret[key]; if (Boolean(val) && typeof val === 'object' && !Array.isArray(val)) { // Sort object keys, not values result[key] = keysSorter(val); } else { result[key] = val; } return result; }, Object.create(null)); }; var stringify = function (obj, opts) { var defaults = { encode: true, strict: true, arrayFormat: 'none' }; opts = index$3(defaults, opts); var formatter = encoderForArrayFormat(opts); return obj ? Object.keys(obj).sort().map(function (key) { var val = obj[key]; if (val === undefined) { return ''; } if (val === null) { return encode(key, opts); } if (Array.isArray(val)) { var result = []; val.slice().forEach(function (val2) { if (val2 === undefined) { return; } result.push(formatter(key, val2, result.length)); }); return result.join('&'); } return encode(key, opts) + '=' + encode(val, opts); }).filter(function (x) { return x.length > 0; }).join('&') : ''; }; var index = { extract: extract, parse: parse, stringify: stringify }; /* global location */ function cloneDeep (o) { return JSON.parse(JSON.stringify(o)) } const browserSupport = console.group != null; function createTestLink (params) { if (typeof location !== 'undefined') { var query = index.parse(location.search); delete query.test; delete query.seed; delete query.args; delete query.repeat; for (var name in params) { if (params[name] != null) { query[name] = params[name]; } } return location.protocol + '//' + location.host + location.pathname + '?' + index.stringify(query) + location.hash } } /* globals location */ class TestHandler { constructor () { this.repeatingRun = 0; this.tests = {}; if (typeof location !== 'undefined') { this.opts = index.parse(location.search); if (this.opts.case != null) { this.opts.case = Number(this.opts.case); } if (this.opts.repeat === 'true') { this.opts.repeat = true; } else if (this.opts.repeat === 'false') { this.opts.repeat = false; } } else { this.opts = {}; } this.opts.repeat = this.opts.repeat !== false; } getRandomSeed () { return this.opts.seed || null } getTestList () { return Object.keys(this.tests).map(name => this.tests[name]) } isTestRunnig () { return this.getTestList().some(test => test.status === 'running') } isSequentialTestRunning () { return this.getTestList().some(test => !test.isParallel() && test.status === 'running') } isParallelTestRunning () { return this.getTestList().some(test => test.isParallel() && test.status === 'running') } get numberOfTests () { return this.getTestList().length } get numberOfCompletedTests () { return this.getTestList().filter(test => test.status === 'done').length } get numberOfSuccessfullTests () { return this.getTestList().filter(test => test.failed === false && test.status === 'done').length } register (test) { if (test.name == null) { throw new Error(` Each test must be defined by a unique function name! E.g. \`test('test description', async function uniqueName () { .. })\` ` ) } if (this.tests[test.name] != null) { throw new Error(` Each test must be defined by a unique function name! => \`test('${test.description}', async function ${test.name} () { .. })\` is already registered! ` ) } if (this.opts.test == null || test.name.indexOf(this.opts.test) >= 0) { this.tests[test.name] = test; if (!this.isTestRunnig() || (test.isParallel() && this.isParallelTestRunning())) { // only if no test is running, or if parallel tests are already running test.run(); } } } _runNextSequentialTest () { let nextSequential = this.getTestList().find( t => t.status === 'pending' && !t.isParallel() ); if (nextSequential != null) { nextSequential.run(); return true } else { return false } } _runNextParallelTests () { let nextParallels = this.getTestList().filter( t => t.status === 'pending' && t.isParallel() ); if (nextParallels.length > 0) { nextParallels.map(t => t.run()); return true } else { return false } } testCompleted (test) { this._runNextParallelTests(); if (!this.isTestRunnig()) { this._runNextSequentialTest(); if (!this.isSequentialTestRunning()) { this.done(); } } } _runRepeatingTests () { let repeatingTests = this.getTestList().filter(t => t.isRepeating()); if (repeatingTests.length > 0 && this.opts.repeat) { this.repeatingRun++; console.log(`%cRunning ${repeatingTests.length} tests again because they use random values.. (${this.repeatingRun}. repeating run)`, 'font-weight:bold'); this.tests = {}; repeatingTests.forEach(t => { this.register(t.clone()); }); this.testCompleted(); } } done () { if (this.numberOfTests === this.numberOfCompletedTests) { if (this.numberOfTests === this.numberOfSuccessfullTests) { if (browserSupport) { console.log('\n%cAll tests passed!', 'font-weight:bold'); console.log('%c ', 'font-size: 1px; padding: 60px 80px; background-size: 170px 120px; line-height: 120px; background-image: url(https://cloud.githubusercontent.com/assets/5553757/25725585/ee1e2ac0-3120-11e7-9401-323c153a99f1.gif)' ); this._runRepeatingTests(); } else { console.log('\n -- All tests passed! --'); } } else { if (browserSupport) { console.log(`\n%cPassed: ${this.numberOfSuccessfullTests} %cFailed: ${this.numberOfTests - this.numberOfSuccessfullTests}`, 'font-weight:bold; color: green', 'font-weight:bold; color:red'); } else { console.log(`\nPassed: ${this.numberOfSuccessfullTests}\nFailed: ${this.numberOfTests - this.numberOfSuccessfullTests}`); } } } } } const testHandler = new TestHandler(); var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var isBrowser = typeof index !== 'undefined'; var environment = { isBrowser: isBrowser }; var Processor$1 = function Processor(options){ this.selfOptions = options || {}; this.pipes = {}; }; Processor$1.prototype.options = function(options) { if (options) { this.selfOptions = options; } return this.selfOptions; }; Processor$1.prototype.pipe = function(name, pipe) { if (typeof name === 'string') { if (typeof pipe === 'undefined') { return this.pipes[name]; } else { this.pipes[name] = pipe; } } if (name && name.name) { pipe = name; if (pipe.processor === this) { return pipe; } this.pipes[pipe.name] = pipe; } pipe.processor = this; return pipe; }; Processor$1.prototype.process = function(input, pipe) { var context = input; context.options = this.options(); var nextPipe = pipe || input.pipe || 'default'; var lastPipe, lastContext; while (nextPipe) { if (typeof context.nextAfterChildren !== 'undefined') { // children processed and coming back to parent context.next = context.nextAfterChildren; context.nextAfterChildren = null; } if (typeof nextPipe === 'string') { nextPipe = this.pipe(nextPipe); } nextPipe.process(context); lastContext = context; lastPipe = nextPipe; nextPipe = null; if (context) { if (context.next) { context = context.next; nextPipe = lastContext.nextPipe || context.pipe || lastPipe; } } } return context.hasResult ? context.result : undefined; }; var Processor_1 = Processor$1; var processor = { Processor: Processor_1 }; var Pipe$1 = function Pipe(name) { this.name = name; this.filters = []; }; Pipe$1.prototype.process = function(input) { if (!this.processor) { throw new Error('add this pipe to a processor before using it'); } var debug = this.debug; var length = this.filters.length; var context = input; for (var index = 0; index < length; index++) { var filter = this.filters[index]; if (debug) { this.log('filter: ' + filter.filterName); } filter(context); if (typeof context === 'object' && context.exiting) { context.exiting = false; break; } } if (!context.next && this.resultCheck) { this.resultCheck(context); } }; Pipe$1.prototype.log = function(msg) { console.log('[jsondiffpatch] ' + this.name + ' pipe, ' + msg); }; Pipe$1.prototype.append = function() { this.filters.push.apply(this.filters, arguments); return this; }; Pipe$1.prototype.prepend = function() { this.filters.unshift.apply(this.filters, arguments); return this; }; Pipe$1.prototype.indexOf = function(filterName) { if (!filterName) { throw new Error('a filter name is required'); } for (var index = 0; index < this.filters.length; index++) { var filter = this.filters[index]; if (filter.filterName === filterName) { return index; } } throw new Error('filter not found: ' + filterName); }; Pipe$1.prototype.list = function() { var names = []; for (var index = 0; index < this.filters.length; index++) { var filter = this.filters[index]; names.push(filter.filterName); } return names; }; Pipe$1.prototype.after = function(filterName) { var index = this.indexOf(filterName); var params = Array.prototype.slice.call(arguments, 1); if (!params.length) { throw new Error('a filter is required'); } params.unshift(index + 1, 0); Array.prototype.splice.apply(this.filters, params); return this; }; Pipe$1.prototype.before = function(filterName) { var index = this.indexOf(filterName); var params = Array.prototype.slice.call(arguments, 1); if (!params.length) { throw new Error('a filter is required'); } params.unshift(index, 0); Array.prototype.splice.apply(this.filters, params); return this; }; Pipe$1.prototype.clear = function() { this.filters.length = 0; return this; }; Pipe$1.prototype.shouldHaveResult = function(should) { if (should === false) { this.resultCheck = null; return; } if (this.resultCheck) { return; } var pipe = this; this.resultCheck = function(context) { if (!context.hasResult) { console.log(context); var error = new Error(pipe.name + ' failed'); error.noResult = true; throw error; } }; return this; }; var Pipe_1 = Pipe$1; var pipe = { Pipe: Pipe_1 }; var Pipe$2 = pipe.Pipe; var Context$1 = function Context(){ }; Context$1.prototype.setResult = function(result) { this.result = result; this.hasResult = true; return this; }; Context$1.prototype.exit = function() { this.exiting = true; return this; }; Context$1.prototype.switchTo = function(next, pipe$$1) { if (typeof next === 'string' || next instanceof Pipe$2) { this.nextPipe = next; } else { this.next = next; if (pipe$$1) { this.nextPipe = pipe$$1; } } return this; }; Context$1.prototype.push = function(child, name) { child.parent = this; if (typeof name !== 'undefined') { child.childName = name; } child.root = this.root || this; child.options = child.options || this.options; if (!this.children) { this.children = [child]; this.nextAfterChildren = this.next || null; this.next = child; } else { this.children[this.children.length - 1].next = child; this.children.push(child); } child.next = this; return this; }; var Context_1 = Context$1; var context = { Context: Context_1 }; var isArray = (typeof Array.isArray === 'function') ? // use native function Array.isArray : // use instanceof operator function(a) { return a instanceof Array; }; function cloneRegExp(re) { var regexMatch = /^\/(.*)\/([gimyu]*)$/.exec(re.toString()); return new RegExp(regexMatch[1], regexMatch[2]); } function clone(arg) { if (typeof arg !== 'object') { return arg; } if (arg === null) { return null; } if (isArray(arg)) { return arg.map(clone); } if (arg instanceof Date) { return new Date(arg.getTime()); } if (arg instanceof RegExp) { return cloneRegExp(arg); } var cloned = {}; for (var name in arg) { if (Object.prototype.hasOwnProperty.call(arg, name)) { cloned[name] = clone(arg[name]); } } return cloned; } var clone_1 = clone; var Context = context.Context; var DiffContext$1 = function DiffContext(left, right) { this.left = left; this.right = right; this.pipe = 'diff'; }; DiffContext$1.prototype = new Context(); DiffContext$1.prototype.setResult = function(result) { if (this.options.cloneDiffValues && typeof result === 'object') { var clone = typeof this.options.cloneDiffValues === 'function' ? this.options.cloneDiffValues : clone_1; if (typeof result[0] === 'object') { result[0] = clone(result[0]); } if (typeof result[1] === 'object') { result[1] = clone(result[1]); } } return Context.prototype.setResult.apply(this, arguments); }; var DiffContext_1 = DiffContext$1; var diff = { DiffContext: DiffContext_1 }; var Context$2 = context.Context; var PatchContext$1 = function PatchContext(left, delta) { this.left = left; this.delta = delta; this.pipe = 'patch'; }; PatchContext$1.prototype = new Context$2(); var PatchContext_1 = PatchContext$1; var patch = { PatchContext: PatchContext_1 }; var Context$3 = context.Context; var ReverseContext$1 = function ReverseContext(delta) { this.delta = delta; this.pipe = 'reverse'; }; ReverseContext$1.prototype = new Context$3(); var ReverseContext_1 = ReverseContext$1; var reverse = { ReverseContext: ReverseContext_1 }; var isArray$1 = (typeof Array.isArray === 'function') ? // use native function Array.isArray : // use instanceof operator function(a) { return a instanceof Array; }; var diffFilter = function trivialMatchesDiffFilter(context) { if (context.left === context.right) { context.setResult(undefined).exit(); return; } if (typeof context.left === 'undefined') { if (typeof context.right === 'function') { throw new Error('functions are not supported'); } context.setResult([context.right]).exit(); return; } if (typeof context.right === 'undefined') { context.setResult([context.left, 0, 0]).exit(); return; } if (typeof context.left === 'function' || typeof context.right === 'function') { throw new Error('functions are not supported'); } context.leftType = context.left === null ? 'null' : typeof context.left; context.rightType = context.right === null ? 'null' : typeof context.right; if (context.leftType !== context.rightType) { context.setResult([context.left, context.right]).exit(); return; } if (context.leftType === 'boolean' || context.leftType === 'number') { context.setResult([context.left, context.right]).exit(); return; } if (context.leftType === 'object') { context.leftIsArray = isArray$1(context.left); } if (context.rightType === 'object') { context.rightIsArray = isArray$1(context.right); } if (context.leftIsArray !== context.rightIsArray) { context.setResult([context.left, context.right]).exit(); return; } if (context.left instanceof RegExp) { if (context.right instanceof RegExp) { context.setResult([context.left.toString(), context.right.toString()]).exit(); } else { context.setResult([context.left, context.right]).exit(); return; } } }; diffFilter.filterName = 'trivial'; var patchFilter = function trivialMatchesPatchFilter(context) { if (typeof context.delta === 'undefined') { context.setResult(context.left).exit(); return; } context.nested = !isArray$1(context.delta); if (context.nested) { return; } if (context.delta.length === 1) { context.setResult(context.delta[0]).exit(); return; } if (context.delta.length === 2) { if (context.left instanceof RegExp) { var regexArgs = /^\/(.*)\/([gimyu]+)$/.exec(context.delta[1]); if (regexArgs) { context.setResult(new RegExp(regexArgs[1], regexArgs[2])).exit(); return; } } context.setResult(context.delta[1]).exit(); return; } if (context.delta.length === 3 && context.delta[2] === 0) { context.setResult(undefined).exit(); return; } }; patchFilter.filterName = 'trivial'; var reverseFilter = function trivialReferseFilter(context) { if (typeof context.delta === 'undefined') { context.setResult(context.delta).exit(); return; } context.nested = !isArray$1(context.delta); if (context.nested) { return; } if (context.delta.length === 1) { context.setResult([context.delta[0], 0, 0]).exit(); return; } if (context.delta.length === 2) { context.setResult([context.delta[1], context.delta[0]]).exit(); return; } if (context.delta.length === 3 && context.delta[2] === 0) { context.setResult([context.delta[0]]).exit(); return; } }; reverseFilter.filterName = 'trivial'; var diffFilter_1 = diffFilter; var patchFilter_1 = patchFilter; var reverseFilter_1 = reverseFilter; var trivial = { diffFilter: diffFilter_1, patchFilter: patchFilter_1, reverseFilter: reverseFilter_1 }; var DiffContext$2 = diff.DiffContext; var PatchContext$2 = patch.PatchContext; var ReverseContext$2 = reverse.ReverseContext; var collectChildrenDiffFilter = function collectChildrenDiffFilter(context) { if (!context || !context.children) { return; } var length = context.children.length; var child; var result = context.result; for (var index = 0; index < length; index++) { child = context.children[index]; if (typeof child.result === 'undefined') { continue; } result = result || {}; result[child.childName] = child.result; } if (result && context.leftIsArray) { result._t = 'a'; } context.setResult(result).exit(); }; collectChildrenDiffFilter.filterName = 'collectChildren'; var objectsDiffFilter = function objectsDiffFilter(context) { if (context.leftIsArray || context.leftType !== 'object') { return; } var name, child, propertyFilter = context.options.propertyFilter; for (name in context.left) { if (!Object.prototype.hasOwnProperty.call(context.left, name)) { continue; } if (propertyFilter && !propertyFilter(name, context)) { continue; } child = new DiffContext$2(context.left[name], context.right[name]); context.push(child, name); } for (name in context.right) { if (!Object.prototype.hasOwnProperty.call(context.right, name)) { continue; } if (propertyFilter && !propertyFilter(name, context)) { continue; } if (typeof context.left[name] === 'undefined') { child = new DiffContext$2(undefined, context.right[name]); context.push(child, name); } } if (!context.children || context.children.length === 0) { context.setResult(undefined).exit(); return; } context.exit(); }; objectsDiffFilter.filterName = 'objects'; var patchFilter$1 = function nestedPatchFilter(context) { if (!context.nested) { return; } if (context.delta._t) { return; } var name, child; for (name in context.delta) { child = new PatchContext$2(context.left[name], context.delta[name]); context.push(child, name); } context.exit(); }; patchFilter$1.filterName = 'objects'; var collectChildrenPatchFilter = function collectChildrenPatchFilter(context) { if (!context || !context.children) { return; } if (context.delta._t) { return; } var length = context.children.length; var child; for (var index = 0; index < length; index++) { child = context.children[index]; if (Object.prototype.hasOwnProperty.call(context.left, child.childName) && child.result === undefined) { delete context.left[child.childName]; } else if (context.left[child.childName] !== child.result) { context.left[child.childName] = child.result; } } context.setResult(context.left).exit(); }; collectChildrenPatchFilter.filterName = 'collectChildren'; var reverseFilter$1 = function nestedReverseFilter(context) { if (!context.nested) { return; } if (context.delta._t) { return; } var name, child; for (name in context.delta) { child = new ReverseContext$2(context.delta[name]); context.push(child, name); } context.exit(); }; reverseFilter$1.filterName = 'objects'; var collectChildrenReverseFilter = function collectChildrenReverseFilter(context) { if (!context || !context.children) { return; } if (context.delta._t) { return; } var length = context.children.length; var child; var delta = {}; for (var index = 0; index < length; index++) { child = context.children[index]; if (delta[child.childName] !== child.result) { delta[child.childName] = child.result; } } context.setResult(delta).exit(); }; collectChildrenReverseFilter.filterName = 'collectChildren'; var collectChildrenDiffFilter_1 = collectChildrenDiffFilter; var objectsDiffFilter_1 = objectsDiffFilter; var patchFilter_1$1 = patchFilter$1; var collectChildrenPatchFilter_1 = collectChildrenPatchFilter; var reverseFilter_1$1 = reverseFilter$1; var collectChildrenReverseFilter_1 = collectChildrenReverseFilter; var nested = { collectChildrenDiffFilter: collectChildrenDiffFilter_1, objectsDiffFilter: objectsDiffFilter_1, patchFilter: patchFilter_1$1, collectChildrenPatchFilter: collectChildrenPatchFilter_1, reverseFilter: reverseFilter_1$1, collectChildrenReverseFilter: collectChildrenReverseFilter_1 }; /* LCS implementation that supports arrays or strings reference: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ var defaultMatch = function(array1, array2, index1, index2) { return array1[index1] === array2[index2]; }; var lengthMatrix = function(array1, array2, match, context) { var len1 = array1.length; var len2 = array2.length; var x, y; // initialize empty matrix of len1+1 x len2+1 var matrix = [len1 + 1]; for (x = 0; x < len1 + 1; x++) { matrix[x] = [len2 + 1]; for (y = 0; y < len2 + 1; y++) { matrix[x][y] = 0; } } matrix.match = match; // save sequence lengths for each coordinate for (x = 1; x < len1 + 1; x++) { for (y = 1; y < len2 + 1; y++) { if (match(array1, array2, x - 1, y - 1, context)) { matrix[x][y] = matrix[x - 1][y - 1] + 1; } else { matrix[x][y] = Math.max(matrix[x - 1][y], matrix[x][y - 1]); } } } return matrix; }; var backtrack = function(matrix, array1, array2, index1, index2, context) { if (index1 === 0 || index2 === 0) { return { sequence: [], indices1: [], indices2: [] }; } if (matrix.match(array1, array2, index1 - 1, index2 - 1, context)) { var subsequence = backtrack(matrix, array1, array2, index1 - 1, index2 - 1, context); subsequence.sequence.push(array1[index1 - 1]); subsequence.indices1.push(index1 - 1); subsequence.indices2.push(index2 - 1); return subsequence; } if (matrix[index1][index2 - 1] > matrix[index1 - 1][index2]) { return backtrack(matrix, array1, array2, index1, index2 - 1, context); } else { return backtrack(matrix, array1, array2, index1 - 1, index2, context); } }; var get = function(array1, array2, match, context) { context = context || {}; var matrix = lengthMatrix(array1, array2, match || defaultMatch, context); var result = backtrack(matrix, array1, array2, array1.length, array2.length, context); if (typeof array1 === 'string' && typeof array2 === 'string') { result.sequence = result.sequence.join(''); } return result; }; var get_1 = get; var lcs = { get: get_1 }; var DiffContext$3 = diff.DiffContext; var PatchContext$3 = patch.PatchContext; var ReverseContext$3 = reverse.ReverseContext; var ARRAY_MOVE = 3; var isArray$2 = (typeof Array.isArray === 'function') ? // use native function Array.isArray : // use instanceof operator function(a) { return a instanceof Array; }; var arrayIndexOf = typeof Array.prototype.indexOf === 'function' ? function(array, item) { return array.indexOf(item); } : function(array, item) { var length = array.length; for (var i = 0; i < length; i++) { if (array[i] === item) { return i; } } return -1; }; function arraysHaveMatchByRef(array1, array2, len1, len2) { for (var index1 = 0; index1 < len1; index1++) { var val1 = array1[index1]; for (var index2 = 0; index2 < len2; index2++) { var val2 = array2[index2]; if (index1 !== index2 && val1 === val2) { return true; } } } } function matchItems(array1, array2, index1, index2, context) { var value1 = array1[index1]; var value2 = array2[index2]; if (value1 === value2) { return true; } if (typeof value1 !== 'object' || typeof value2 !== 'object') { return false; } var objectHash = context.objectHash; if (!objectHash) { // no way to match objects was provided, try match by position return context.matchByPosition && index1 === index2; } var hash1; var hash2; if (typeof index1 === 'number') { context.hashCache1 = context.hashCache1 || []; hash1 = context.hashCache1[index1]; if (typeof hash1 === 'undefined') { context.hashCache1[index1] = hash1 = objectHash(value1, index1); } } else { hash1 = objectHash(value1); } if (typeof hash1 === 'undefined') { return false; } if (typeof index2 === 'number') { context.hashCache2 = context.hashCache2 || []; hash2 = context.hashCache2[index2]; if (typeof hash2 === 'undefined') { context.hashCache2[index2] = hash2 = objectHash(value2, index2); } } else { hash2 = objectHash(value2); } if (typeof hash2 === 'undefined') { return false; } return hash1 === hash2; } var diffFilter$1 = function arraysDiffFilter(context) { if (!context.leftIsArray) { return; } var matchContext = { objectHash: context.options && context.options.objectHash, matchByPosition: context.options && context.options.matchByPosition }; var commonHead = 0; var commonTail = 0; var index; var index1; var index2; var array1 = context.left; var array2 = context.right; var len1 = array1.length; var len2 = array2.length; var child; if (len1 > 0 && len2 > 0 && !matchContext.objectHash && typeof matchContext.matchByPosition !== 'boolean') { matchContext.matchByPosition = !arraysHaveMatchByRef(array1, array2, len1, len2); } // separate common head while (commonHead < len1 && commonHead < len2 && matchItems(array1, array2, commonHead, commonHead, matchContext)) { index = commonHead; child = new DiffContext$3(context.left[index], context.right[index]); context.push(child, index); commonHead++; } // separate common tail while (commonTail + commonHead < len1 && commonTail + commonHead < len2 && matchItems(array1, array2, len1 - 1 - commonTail, len2 - 1 - commonTail, matchContext)) { index1 = len1 - 1 - commonTail; index2 = len2 - 1 - commonTail; child = new DiffContext$3(context.left[index1], context.right[index2]); context.push(child, index2); commonTail++; } var result; if (commonHead + commonTail === len1) { if (len1 === len2) { // arrays are identical context.setResult(undefined).exit(); return; } // trivial case, a block (1 or more consecutive items) was added result = result || { _t: 'a' }; for (index = commonHead; index < len2 - commonTail; index++) { result[index] = [array2[index]]; } context.setResult(result).exit(); return; } if (commonHead + commonTail === len2) { // trivial case, a block (1 or more consecutive items) was removed result = result || { _t: 'a' }; for (index = commonHead; index < len1 - commonTail; index++) { result['_' + index] = [array1[index], 0, 0]; } context.setResult(result).exit(); return; } // reset hash cache delete matchContext.hashCache1; delete matchContext.hashCache2; // diff is not trivial, find the LCS (Longest Common Subsequence) var trimmed1 = array1.slice(commonHead, len1 - commonTail); var trimmed2 = array2.slice(commonHead, len2 - commonTail); var seq = lcs.get( trimmed1, trimmed2, matchItems, matchContext ); var removedItems = []; result = result || { _t: 'a' }; for (index = commonHead; index < len1 - commonTail; index++) { if (arrayIndexOf(seq.indices1, index - commonHead) < 0) { // removed result['_' + index] = [array1[index], 0, 0]; removedItems.push(index); } } var detectMove = true; if (context.options && context.options.arrays && context.options.arrays.detectMove === false) { detectMove = false; } var includeValueOnMove = false; if (context.options && context.options.arrays && context.options.arrays.includeValueOnMove) { includeValueOnMove = true; } var removedItemsLength = removedItems.length; for (index = commonHead; index < len2 - commonTail; index++) { var indexOnArray2 = arrayIndexOf(seq.indices2, index - commonHead); if (indexOnArray2 < 0) { // added, try to match with a removed item and register as position move var isMove = false; if (detectMove && removedItemsLength > 0) { for (var removeItemIndex1 = 0; removeItemIndex1 < removedItemsLength; removeItemIndex1++) { index1 = removedItems[removeItemIndex1]; if (matchItems(trimmed1, trimmed2, index1 - commonHead, index - commonHead, matchContext)) { // store position move as: [originalValue, newPosition, ARRAY_MOVE] result['_' + index1].splice(1, 2, index, ARRAY_MOVE); if (!includeValueOnMove) { // don't include moved value on diff, to save bytes result['_' + index1][0] = ''; } index2 = index; child = new DiffContext$3(context.left[index1], context.right[index2]); context.push(child, index2); removedItems.splice(removeItemIndex1, 1); isMove = true; break; } } } if (!isMove) { // added result[index] = [array2[index]]; } } else { // match, do inner diff index1 = seq.indices1[indexOnArray2] + commonHead; index2 = seq.indices2[indexOnArray2] + commonHead; child = new DiffContext$3(context.left[index1], context.right[index2]); context.push(child, index2); } } context.setResult(result).exit(); }; diffFilter$1.filterName = 'arrays'; var compare = { numerically: function(a, b) { return a - b; }, numericallyBy: function(name) { return function(a, b) { return a[name] - b[name]; }; } }; var patchFilter$2 = function nestedPatchFilter(context) { if (!context.nested) { return; } if (context.delta._t !== 'a') { return; } var index, index1; var delta = context.delta; var array = context.left; // first, separate removals, insertions and modifications var toRemove = []; var toInsert = []; var toModify = []; for (index in delta) { if (index !== '_t') { if (index[0] === '_') { // removed item from original array if (delta[index][2] === 0 || delta[index][2] === ARRAY_MOVE) { toRemove.push(parseInt(index.slice(1), 10)); } else { throw new Error('only removal or move can be applied at original array indices' + ', invalid diff type: ' + delta[index][2]); } } else { if (delta[index].length === 1) { // added item at new array toInsert.push({ index: parseInt(index, 10), value: delta[index][0] }); } else { // modified item at new array toModify.push({ index: parseInt(index, 10), delta: delta[index] }); } } } } // remove items, in reverse order to avoid sawing our own floor toRemove = toRemove.sort(compare.numerically); for (index = toRemove.length - 1; index >= 0; index--) { index1 = toRemove[index]; var indexDiff = delta['_' + index1]; var removedValue = array.splice(index1, 1)[0]; if (indexDiff[2] === ARRAY_MOVE) { // reinsert later toInsert.push({ index: indexDiff[1], value: removedValue }); } } // insert items, in reverse order to avoid moving our own floor toInsert = toInsert.sort(compare.numericallyBy('index')); var toInsertLength = toInsert.length; for (index = 0; index < toInsertLength; index++) { var insertion = toInsert[index]; array.splice(insertion.index, 0, insertion.value); } // apply modifications var toModifyLength = toModify.length; var child; if (toModifyLength > 0) { for (index = 0; index < toModifyLength; index++) { var modification = toModify[index]; child = new PatchContext$3(context.left[modification.index], modification.delta); context.push(child, modification.index); } } if (!context.children) { context.setResult(context.left).exit(); return; } context.exit(); }; patchFilter$2.filterName = 'arrays'; var collectChildrenPatchFilter$1 = function collectChildrenPatchFilter(context) { if (!context || !context.children) { return; } if (context.delta._t !== 'a') { return; } var length = context.children.length; var child; for (var index = 0; index < length; index++) { child = context.children[index]; context.left[child.childName] = child.result; } context.setResult(context.left).exit(); }; collectChildrenPatchFilter$1.filterName = 'arraysCollectChildren'; var reverseFilter$2 = function arraysReverseFilter(context) { if (!context.nested) { if (context.delta[2] === ARRAY_MOVE) { context.newName = '_' + context.delta[1]; context.setResult([context.delta[0], parseInt(context.childName.substr(1), 10), ARRAY_MOVE]).exit(); } return; } if (context.delta._t !== 'a') { return; } var name, child; for (name in context.delta) { if (name === '_t') { continue; } child = new ReverseContext$3(context.delta[name]); context.push(child, name); } context.exit(); }; reverseFilter$2.filterName = 'arrays'; var reverseArrayDeltaIndex = function(delta, index, itemDelta) { if (typeof index === 'string' && index[0] === '_') { return parseInt(index.substr(1), 10); } else if (isArray$2(itemDelta) && itemDelta[2] === 0) { return '_' + index; } var reverseIndex = +index; for (var deltaIndex in delta) { var deltaItem = delta[deltaIndex]; if (isArray$2(deltaItem)) { if (deltaItem[2] === ARRAY_MOVE) { var moveFromIndex = parseInt(deltaIndex.substr(1), 10); var moveToIndex = deltaItem[1]; if (moveToIndex === +index) { return moveFromIndex; } if (moveFromIndex <= reverseIndex && moveToIndex > reverseIndex) { reverseIndex++; } else if (moveFromIndex >= reverseIndex && moveToIndex < reverseIndex) { reverseIndex--; } } else if (deltaItem[2] === 0) { var deleteIndex = parseInt(deltaIndex.substr(1), 10); if (deleteIndex <= reverseIndex) { reverseIndex++; } } else if (deltaItem.length === 1 && deltaIndex <= reverseIndex) { reverseIndex--; } } } return reverseIndex; }; var collectChildrenReverseFilter$1 = function collectChildrenReverseFilter(context) { if (!context || !context.children) { return; } if (context.delta._t !== 'a') { return; } var length = context.children.length; var child; var delta = { _t: 'a' }; for (var index = 0; index < length; index++) { child = context.children[index]; var name = child.newName; if (typeof name === 'undefined') { name = reverseArrayDeltaIndex(context.delta, child.childName, child.result); } if (delta[name] !== child.result) { delta[name] = child.result; } } context.setResult(delta).exit(); }; collectChildrenReverseFilter$1.filterName = 'arraysCollectChildren'; var diffFilter_1$1 = diffFilter$1; var patchFilter_1$2 = patchFilter$2; var collectChildrenPatchFilter_1$1 = collectChildrenPatchFilter$1; var reverseFilter_1$2 = reverseFilter$2; var collectChildrenReverseFilter_1$1 = collectChildrenReverseFilter$1; var arrays = { diffFilter: diffFilter_1$1, patchFilter: patchFilter_1$2, collectChildrenPatchFilter: collectChildrenPatchFilter_1$1, reverseFilter: reverseFilter_1$2, collectChildrenReverseFilter: collectChildrenReverseFilter_1$1 }; var diffFilter$2 = function datesDiffFilter(context) { if (context.left instanceof Date) { if (context.right instanceof Date) { if (context.left.getTime() !== context.right.getTime()) { context.setResult([context.left, context.right]); } else { context.setResult(undefined); } } else { context.setResult([context.left, context.right]); } context.exit(); } else if (context.right instanceof Date) { context.setResult([context.left, context.right]).exit(); } }; diffFilter$2.filterName = 'dates'; var diffFilter_1$2 = diffFilter$2; var dates = { diffFilter: diffFilter_1$2 }; /* global diff_match_patch */ var TEXT_DIFF = 2; var DEFAULT_MIN_LENGTH = 60; var cachedDiffPatch = null; var getDiffMatchPatch = function(required) { /*jshint camelcase: false */ if (!cachedDiffPatch) { var instance; if (typeof diff_match_patch !== 'undefined') { // already loaded, probably a browser instance = typeof diff_match_patch === 'function' ? new diff_match_patch() : new diff_match_patch.diff_match_patch(); } else if (typeof commonjsRequire === 'function') { try { var dmpModuleName = 'diff_match_patch_uncompressed'; var dmp = commonjsRequire('../../public/external/' + dmpModuleName); instance = new dmp.diff_match_patch(); } catch (err) { instance = null; } } if (!instance) { if (!required) { return null; } var error = new Error('text diff_match_patch library not found'); error.diff_match_patch_not_found = true; throw error; } cachedDiffPatch = { diff: function(txt1, txt2) { return instance.patch_toText(instance.patch_make(txt1, txt2)); }, patch: function(txt1, patch) { var results = instance.patch_apply(instance.patch_fromText(patch), txt1); for (var i = 0; i < results[1].length; i++) { if (!results[1][i]) { var error = new Error('text patch failed'); error.textPatchFailed = true; } } return results[0]; } }; } return cachedDiffPatch; }; var diffFilter$3 = function textsDiffFilter(context) { if (context.leftType !== 'string') { return; } var minLength = (context.options && context.options.textDiff && context.options.textDiff.minLength) || DEFAULT_MIN_LENGTH; if (context.left.length < minLength || context.right.length < minLength) { context.setResult([context.left, context.right]).exit(); return; } // large text, try to use a text-diff algorithm var diffMatchPatch = getDiffMatchPatch(); if (!diffMatchPatch) { // diff-match-patch library not available, fallback to regular string replace context.setResult([context.left, context.right]).exit(); return; } var diff = diffMatchPatch.diff; context.setResult([diff(context.left, context.right), 0, TEXT_DIFF]).exit(); }; diffFilter$3.filterName = 'texts'; var patchFilter$3 = function textsPatchFilter(context) { if (context.nested) { return; } if (context.delta[2] !== TEXT_DIFF) { return; } // text-diff, use a text-patch algorithm var patch = getDiffMatchPatch(true).patch; context.setResult(patch(context.left, context.delta[0])).exit(); }; patchFilter$3.filterName = 'texts'; var textDeltaReverse = function(delta) { var i, l, lines, line, lineTmp, header = null, headerRegex = /^@@ +\-(\d+),(\d+) +\+(\d+),(\d+) +@@$/, lineHeader, lineAdd, lineRemove; lines = delta.split('\n'); for (i = 0, l = lines.length; i < l; i++) { line = lines[i]; var lineStart = line.slice(0, 1); if (lineStart === '@') { header = headerRegex.exec(line); lineHeader = i; lineAdd = null; lineRemove = null; // fix header lines[lineHeader] = '@@ -' + header[3] + ',' + header[4] + ' +' + header[1] + ',' + header[2] + ' @@'; } else if (lineStart === '+') { lineAdd = i; lines[i] = '-' + lines[i].slice(1); if (lines[i - 1].slice(0, 1) === '+') { // swap lines to keep default order (-+) lineTmp = lines[i]; lines[i] = lines[i - 1]; lines[i - 1] = lineTmp; } } else if (lineStart === '-') { lineRemove = i; lines[i] = '+' + lines[i].slice(1); } } return lines.join('\n'); }; var reverseFilter$3 = function textsReverseFilter(context) { if (context.nested) { return; } if (context.delta[2] !== TEXT_DIFF) { return; } // text-diff, use a text-diff algorithm context.setResult([textDeltaReverse(context.delta[0]), 0, TEXT_DIFF]).exit(); }; reverseFilter$3.filterName = 'texts'; var diffFilter_1$3 = diffFilter$3; var patchFilter_1$3 = patchFilter$3; var reverseFilter_1$3 = reverseFilter$3; var texts = { diffFilter: diffFilter_1$3, patchFilter: patchFilter_1$3, reverseFilter: reverseFilter_1$3 }; var Processor = processor.Processor; var Pipe = pipe.Pipe; var DiffContext = diff.DiffContext; var PatchContext = patch.PatchContext; var ReverseContext = reverse.ReverseContext; var DiffPatcher = function DiffPatcher(options) { this.processor = new Processor(options); this.processor.pipe(new Pipe('diff').append( nested.collectChildrenDiffFilter, trivial.diffFilter, dates.diffFilter, texts.diffFilter, nested.objectsDiffFilter, arrays.diffFilter ).shouldHaveResult()); this.processor.pipe(new Pipe('patch').append( nested.collectChildrenPatchFilter, arrays.collectChildrenPatchFilter, trivial.patchFilter, texts.patchFilter, nested.patchFilter, arrays.patchFilter ).shouldHaveResult()); this.processor.pipe(new Pipe('reverse').append( nested.collectChildrenReverseFilter, arrays.collectChildrenReverseFilter, trivial.reverseFilter, texts.reverseFilter, nested.reverseFilter, arrays.reverseFilter ).shouldHaveResult()); }; DiffPatcher.prototype.options = function() { return this.processor.options.apply(this.processor, arguments); }; DiffPatcher.prototype.diff = function(left, right) { return this.processor.process(new DiffContext(left, right)); }; DiffPatcher.prototype.patch = function(left, delta) { return this.processor.process(new PatchContext(left, delta)); }; DiffPatcher.prototype.reverse = function(delta) { return this.processor.process(new ReverseContext(delta)); }; DiffPatcher.prototype.unpatch = function(right, delta) { return this.patch(right, this.reverse(delta)); }; DiffPatcher.prototype.clone = function(value) { return clone_1(value); }; var DiffPatcher_1 = DiffPatcher; var diffpatcher = { DiffPatcher: DiffPatcher_1 }; // use as 2nd parameter for JSON.parse to revive Date instances var dateReviver = function dateReviver(key, value) { var parts; if (typeof value === 'string') { parts = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d*))?(Z|([+\-])(\d{2}):(\d{2}))$/.exec(value); if (parts) { return new Date(Date.UTC(+parts[1], +parts[2] - 1, +parts[3], +parts[4], +parts[5], +parts[6], +(parts[7] || 0))); } } return value; }; var main = createCommonjsModule(function (module, exports) { var DiffPatcher = diffpatcher.DiffPatcher; exports.DiffPatcher = DiffPatcher; exports.create = function(options){ return new DiffPatcher(options); }; exports.dateReviver = dateReviver; var defaultInstance; exports.diff = function() { if (!defaultInstance) { defaultInstance = new DiffPatcher(); } return defaultInstance.diff.apply(defaultInstance, arguments); }; exports.patch = function() { if (!defaultInstance) { defaultInstance = new DiffPatcher(); } return defaultInstance.patch.apply(defaultInstance, arguments); }; exports.unpatch = function() { if (!defaultInstance) { defaultInstance = new DiffPatcher(); } return defaultInstance.unpatch.apply(defaultInstance, arguments); }; exports.reverse = function() { if (!defaultInstance) { defaultInstance = new DiffPatcher(); } return defaultInstance.reverse.apply(defaultInstance, arguments); }; exports.clone = function() { if (!defaultInstance) { defaultInstance = new DiffPatcher(); } return defaultInstance.clone.apply(defaultInstance, arguments); }; if (environment.isBrowser) { exports.homepage = '{{package-homepage}}'; exports.version = '{{package-version}}'; } else { var packageInfoModuleName = '../package.json'; var packageInfo = commonjsRequire(packageInfoModuleName); exports.homepage = packageInfo.homepage; exports.version = packageInfo.version; var formatterModuleName = './formatters'; var formatters = commonjsRequire(formatterModuleName); exports.formatters = formatters; // shortcut for console exports.console = formatters.console; } }); var isArray$3 = (typeof Array.isArray === 'function') ? // use native function Array.isArray : // use instanceof operator function(a) { return a instanceof Array; }; var getObjectKeys = typeof Object.keys === 'function' ? function(obj) { return Object.keys(obj); } : function(obj) { var names = []; for (var property in obj) { if (Object.prototype.hasOwnProperty.call(obj, property)) { names.push(property); } } return names; }; var trimUnderscore = function(str) { if (str.substr(0, 1) === '_') { return str.slice(1); } return str; }; var arrayKeyToSortNumber = function(key) { if (key === '_t') { return -1; } else { if (key.substr(0, 1) === '_') { return parseInt(key.slice(1), 10); } else { return parseInt(key, 10) + 0.1; } } }; var arrayKeyComparer = function(key1, key2) { return arrayKeyToSortNumber(key1) - arrayKeyToSortNumber(key2); }; var BaseFormatter$1 = function BaseFormatter() {}; BaseFormatter$1.prototype.format = function(delta, left) { var context = {}; this.prepareContext(context); this.recurse(context, delta, left); return this.finalize(context); }; BaseFormatter$1.prototype.prepareContext = function(context) { context.buffer = []; context.out = function() { this.buffer.push.apply(this.buffer, arguments); }; }; BaseFormatter$1.prototype.typeFormattterNotFound = function(context, deltaType) { throw new Error('cannot format delta type: ' + deltaType); }; BaseFormatter$1.prototype.typeFormattterErrorFormatter = function(context, err) { return err.toString(); }; BaseFormatter$1.prototype.finalize = function(context) { if (isArray$3(context.buffer)) { return context.buffer.join(''); } }; BaseFormatter$1.prototype.recurse = function(context, delta, left, key, leftKey, movedFrom, isLast) { var useMoveOriginHere = delta && movedFrom; var leftValue = useMoveOriginHere ? movedFrom.value : left; if (typeof delta === 'undefined' && typeof key === 'undefined') { return undefined; } var type = this.getDeltaType(delta, movedFrom); var nodeType = type === 'node' ? (delta._t === 'a' ? 'array' : 'object') : ''; if (typeof key !== 'undefined') { this.nodeBegin(context, key, leftKey, type, nodeType, isLast); } else { this.rootBegin(context, type, nodeType); } var typeFormattter; try { typeFormattter = this['format_' + type] || this.typeFormattterNotFound(context, type); typeFormattter.call(this, context, delta, leftValue, key, leftKey, movedFrom); } catch (err) { this.typeFormattterErrorFormatter(context, err, delta, leftValue, key, leftKey, movedFrom); if (typeof console !== 'undefined' && console.error) { console.error(err.stack); } } if (typeof key !== 'undefined') { this.nodeEnd(context, key, leftKey, type, nodeType, isLast); } else { this.rootEnd(context, type, nodeType); } }; BaseFormatter$1.prototype.formatDeltaChildren = function(context, delta, left) { var self = this; this.forEachDeltaKey(delta, left, function(key, leftKey, movedFrom, isLast) { self.recurse(context, delta[key], left ? left[leftKey] : undefined, key, leftKey, movedFrom, isLast); }); }; BaseFormatter$1.prototype.forEachDeltaKey = function(delta, left, fn) { var keys = getObjectKeys(delta); var arrayKeys = delta._t === 'a'; var moveDestinations = {}; var name; if (typeof left !== 'undefined') { for (name in left) { if (Object.prototype.hasOwnProperty.call(left, name)) { if (typeof delta[name] === 'undefined' && ((!arrayKeys) || typeof delta['_' + name] === 'undefined')) { keys.push(name); } } } } // look for move destinations for (name in delta) { if (Object.prototype.hasOwnProperty.call(delta, name)) { var value = delta[name]; if (isArray$3(value) && value[2] === 3) { moveDestinations[value[1].toString()] = { key: name, value: left && left[parseInt(name.substr(1))] }; if (this.includeMoveDestinations !== false) { if ((typeof left === 'undefined') && (typeof delta[value[1]] === 'undefined')) { keys.push(value[1].toString()); } } } } } if (arrayKeys) { keys.sort(arrayKeyComparer); } else { keys.sort(); } for (var index = 0, length = keys.length; index < length; index++) { var key = keys[index]; if (arrayKeys && key === '_t') { continue; } var leftKey = arrayKeys ? (typeof key === 'number' ? key : parseInt(trimUnderscore(key), 10)) : key; var isLast = (index === length - 1); fn(key, leftKey, moveDestinations[leftKey], isLast); } }; BaseFormatter$1.prototype.getDeltaType = function(delta, movedFrom) { if (typeof delta === 'undefined') { if (typeof movedFrom !== 'undefined') { return 'movedestination'; } return 'unchanged'; } if (isArray$3(delta)) { if (delta.length === 1) { return 'added'; } if (delta.length === 2) { return 'modified'; } if (delta.length === 3 && delta[2] === 0) { return 'deleted'; } if (delta.length === 3 && delta[2] === 2) { return 'textdiff'; } if (delta.length === 3 && delta[2] === 3) { return 'moved'; } } else if (typeof delta === 'object') { return 'node'; } return 'unknown'; }; BaseFormatter$1.prototype.parseTextDiff = function(value) { var output = []; var lines = value.split('\n@@ '); for (var i = 0, l = lines.length; i < l; i++) { var line = lines[i]; var lineOutput = { pieces: [] }; var location = /^(?:@@ )?[-+]?(\d+),(\d+)/.exec(line).slice(1); lineOutput.location = { line: location[0], chr: location[1] }; var pieces = line.split('\n').slice(1); for (var pieceIndex = 0, piecesLength = pieces.length; pieceIndex < piecesLength; pieceIndex++) { var piece = pieces[pieceIndex]; if (!piece.length) { continue; } var pieceOutput = { type: 'context' }; if (piece.substr(0, 1) === '+') { pieceOutput.type = 'added'; } else if (piece.substr(0, 1) === '-') { pieceOutput.type = 'deleted'; } pieceOutput.text = piece.slice(1); lineOutput.pieces.push(pieceOutput); } output.push(lineOutput); } return output; }; var BaseFormatter_1 = BaseFormatter$1; var base = { BaseFormatter: BaseFormatter_1 }; var BaseFormatter = base.BaseFormatter; var colors = { added: 'color:green', deleted: 'color:red', movedestination: 'color:gray', moved: 'color:blue', unchanged: 'hide', error: 'background:red', textDiffLine: 'color:gray' }; function ConsoleFormatter () { this.includeMoveDestinations = false; } ConsoleFormatter.prototype = new BaseFormatter(); ConsoleFormatter.prototype.finalize = function (context) { var match = context.styles.length === 0; var styles = context.styles; var buffer = context.buffer .join('') .split('\n'); buffer = buffer .filter((t, i) => !(t.match(/^ +$/) && buffer[i] === t)); var styleCounter = 0; for (var i = 0; i < buffer.length; i++) { var b = buffer[i]; var styleOccurences = b.split('%c').length - 1; if (styleOccurences === 0) { buffer[i] = '%c' + b; styles.splice(styleCounter, 0, ''); styleCounter++; } else { styleCounter += styleOccurences; } } var text = buffer.join('\n'); return { logArguments: [text].concat(styles), match: match } }; ConsoleFormatter.prototype.prepareContext = function (context) { BaseFormatter.prototype.prepareContext.call(this, context); context.styles = context.styles || []; context.indent = function (levels) { this.indentLevel = (this.indentLevel || 0) + (typeof levels === 'undefined' ? 1 : levels); this.indentPad = new Array(this.indentLevel + 1).join(' '); this.outLine(); }; context.outLine = function () { this.buffer.push('\n' + (this.indentPad || '')); }; context.out = function () { for (var i = 0, l = arguments.length; i < l; i++) { var lines = arguments[i].split('\n'); var text = lines.join('\n' + (this.indentPad || '')); if (this.color == null || this.color[0] !== 'hide') { if (this.color && this.color[0]) { text = '%c' + text; this.styles.push(this.color[0]); } this.buffer.push(text); } } }; context.pushColor = function (color) { this.color = this.color || []; this.color.unshift(color); }; context.popColor = function () { this.color = this.color || []; this.color.shift(); }; }; ConsoleFormatter.prototype.typeFormattterErrorFormatter = function (context, err) { context.pushColor(colors.error); context.out('[ERROR]' + err); context.popColor(); }; ConsoleFormatter.prototype.formatValue = function (context, value) { context.out(JSON.stringify(value, null, 2)); }; ConsoleFormatter.prototype.formatTextDiffString = function (context, value) { var lines = this.parseTextDiff(value); context.indent(); for (var i = 0, l = lines.length; i < l; i++) { var line = lines[i]; context.pushColor(colors.textDiffLine); context.out(line.location.line + ',' + line.location.chr + ' '); context.popColor(); var pieces = line.pieces; for (var pieceIndex = 0, piecesLength = pieces.length; pieceIndex < piecesLength; pieceIndex++) { var piece = pieces[pieceIndex]; context.pushColor(colors[piece.type]); context.out(piece.text); context.popColor(); } if (i < l - 1) { context.outLine(); } } context.indent(-1); }; ConsoleFormatter.prototype.rootBegin = function (context, type, nodeType) { context.pushColor(colors[type]); if (type === 'node') { context.out(nodeType === 'array' ? '[' : '{'); context.indent(); } }; ConsoleFormatter.prototype.rootEnd = function (context, type, nodeType) { if (type === 'node') { context.indent(-1); context.out(nodeType === 'array' ? ']' : '}'); } context.popColor(); }; ConsoleFormatter.prototype.nodeBegin = function (context, key, leftKey, type, nodeType) { context.pushColor(colors[type]); context.out(leftKey + ': '); if (type === 'node') { context.out(nodeType === 'array' ? '[' : '{'); context.indent(); } }; ConsoleFormatter.prototype.nodeEnd = function (context, key, leftKey, type, nodeType, isLast) { if (type === 'node') { context.indent(-1); context.out(nodeType === 'array' ? ']' : '}' + (isLast ? '' : ',')); } if (!isLast) { context.outLine(); } context.popColor(); }; /* jshint camelcase: false */ ConsoleFormatter.prototype.format_unchanged = function (context, delta, left) { if (typeof left === 'undefined') { return } this.formatValue(context, left); }; ConsoleFormatter.prototype.format_movedestination = function (context, delta, left) { if (typeof left === 'undefined') { return } this.formatValue(context, left); }; ConsoleFormatter.prototype.format_node = function (context, delta, left) { // recurse this.formatDeltaChildren(context, delta, left); }; ConsoleFormatter.prototype.format_added = function (context, delta) { this.formatValue(context, delta[0]); }; ConsoleFormatter.prototype.format_modified = function (context, delta) { context.pushColor(colors.deleted); this.formatValue(context, delta[0]); context.popColor(); context.out(' => '); context.pushColor(colors.added); this.formatValue(context, delta[1]); context.popColor(); }; ConsoleFormatter.prototype.format_deleted = function (context, delta) { this.formatValue(context, delta[0]); }; ConsoleFormatter.prototype.format_moved = function (context, delta) { context.out('==> ' + delta[1]); }; ConsoleFormatter.prototype.format_textdiff = function (context, delta) { this.formatTextDiffString(context, delta[0]); }; var defaultInstance; function format (delta, left) { if (!defaultInstance) { defaultInstance = new ConsoleFormatter(); } return defaultInstance.format(delta, left) } class Logger { constructor () { this.buffer = []; this.failed = false; this.errors = 0; } fail () { this.failed = true; this.errors++; } log () { this.buffer.push({ f: 'log', args: Array.prototype.slice.call(arguments) }); } error () { this.fail(); var args = Array.prototype.slice.call(arguments); if (typeof args[0] === 'string') { args[0] = '%c' + args[0]; args.splice(1, 0, 'color:red'); } args.push(new Error().stack); this.buffer.push({ f: 'log', args: args }); } assert (condition, output) { if (!condition) { this.fail(); } this.buffer.push({ f: 'log', args: [`%c${output}`, `color: ${condition ? 'green' : 'red'}`] }); } group (f, ...args) { if (args.length === 0 || typeof args[0] !== 'string') { args.unshift('Group'); } args[0] = '%c' + args[0]; this.buffer.push({ f: 'groupCollapsed', args: args }); var eBeforeExecution = this.errors; try { f(); } catch (e) { this.fail(); this.buffer.push({ f: 'log', args: ['%cUncaught ' + e.stack, 'color:red'] }); } if (eBeforeExecution === this.errors) { args.splice(1, 0, ''); } else { args.splice(1, 0, 'color: red'); } this.buffer.push({ f: 'groupEnd' }); } async asyncGroup (f, ...args) { if (args.length === 0 || typeof args[0] !== 'string') { args.unshift('Group'); } args[0] = '%c' + args[0]; this.buffer.push({ f: 'groupCollapsed', args: args }); var eBeforeExecution = this.errors; try { await f(); } catch (e) { this.fail(); this.buffer.push({ f: 'log', args: ['%cUncaught ' + e.stack, 'color:red'] }); } if (eBeforeExecution === this.errors) { args.splice(1, 0, ''); } else { args.splice(1, 0, 'color: red'); } this.buffer.push({ f: 'groupEnd' }); } compare (o1, o2, name) { var arg1 = typeof o1 === 'string' ? `"${o1}"` : cloneDeep(o1); var arg2 = typeof o2 === 'string' ? `"${o2}"` : cloneDeep(o2); this.group(() => { var delta = main.diff(o1, o2); var res = format(delta, o1); if (!res.match) { this.fail(); } this.log.apply(this, res.logArguments); }, name, arg1, arg2); } } class TestCase extends Logger { constructor (testDescription, testFunction, location, valueGenerators, opts) { super(); this.valueGenerators = valueGenerators; this.description = testDescription; this.testFunction = testFunction; this.location = location; this.name = testFunction.name; this._seed = null; this.status = 'pending'; this.opts = opts || {}; } isRepeating () { return this._seed != null && testHandler.getRandomSeed() === null } isParallel () { return this.opts.parallel === true } clone () { return new TestCase(this.description, this.testFunction, this.valueGenerators, this.opts) } run () { this.status = 'running'; var __iterateOverGenerators = async (gens, args, argcase) => { if (gens.length === 0) { argcase.i++; if (testHandler.opts.case == null || testHandler.opts.case === argcase.i) { var url = createTestLink({ test: this.name, seed: this._seed, case: argcase.i, repeat: this.isRepeating() }); args.push(url); await this.asyncGroup(async () => { await this.testFunction(this, ...args); }, 'Arguments:', ...args); } } else { var gen = gens.shift(); for (var arg of gen) { await __iterateOverGenerators(gens.slice(), args.slice().concat([arg]), argcase); } } }; var __testStarter = () => { var test; if (this.valueGenerators.length > 0) { test = __iterateOverGenerators(this.valueGenerators, [], { i: 0 }); } else { test = this.testFunction(this); } test.then(async () => { this.status = 'done'; await this.print(); testHandler.testCompleted(this); }, async (err) => { this.status = 'done'; this.failed = true; this.buffer.push({ f: 'log', args: ['%cUncaught ' + err.stack, 'color: red'] }); await this.print(); testHandler.testCompleted(this); }); }; setTimeout(__testStarter, 0); } getSeed () { if (this._seed == null) { this._seed = testHandler.getRandomSeed() || Math.random(); } return this._seed } print () { if (browserSupport) { var url = createTestLink({ test: this.name, seed: this._seed, repeat: false }); console.groupCollapsed( `%c${testHandler.numberOfCompletedTests}/${testHandler.numberOfTests}%c ${this.failed ? 'X' : '√'} ${this.description}`, 'font-weight: bold', `color: ${this.failed ? 'red' : 'green'}` ); console.log(`%cLocation: ${this.location.fileName}:${this.location.lineNumber}\nRun test again: ${url}`, 'color: grey; font-style: italic; font-size: x-small'); this.buffer.forEach(function (b) { console[b.f].apply(console, b.args); }); console.groupEnd(); } else { console.log( `${testHandler.numberOfCompletedTests}/${testHandler.numberOfTests} ${this.failed ? 'X' : '√'} ${this.description}` ); } } } var stackframe = createCommonjsModule(function (module, exports) { (function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { undefined('stackframe', [], factory); } else { module.exports = factory(); } }(commonjsGlobal, function() { 'use strict'; function _isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); } function _capitalize(str) { return str[0].toUpperCase() + str.substring(1); } function _getter(p) { return function() { return this[p]; }; } var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel']; var numericProps = ['columnNumber', 'lineNumber']; var stringProps = ['fileName', 'functionName', 'source']; var arrayProps = ['args']; var props = booleanProps.concat(numericProps, stringProps, arrayProps); function StackFrame(obj) { if (obj instanceof Object) { for (var i = 0; i < props.length; i++) { if (obj.hasOwnProperty(props[i]) && obj[props[i]] !== undefined) { this['set' + _capitalize(props[i])](obj[props[i]]); } } } } StackFrame.prototype = { getArgs: function() { return this.args; }, setArgs: function(v) { if (Object.prototype.toString.call(v) !== '[object Array]') { throw new TypeError('Args must be an Array'); } this.args = v; }, getEvalOrigin: function() { return this.evalOrigin; }, setEvalOrigin: function(v) { if (v instanceof StackFrame) { this.evalOrigin = v; } else if (v instanceof Object) { this.evalOrigin = new StackFrame(v); } else { throw new TypeError('Eval Origin must be an Object or StackFrame'); } }, toString: function() { var functionName = this.getFunctionName() || '{anonymous}'; var args = '(' + (this.getArgs() || []).join(',') + ')'; var fileName = this.getFileName() ? ('@' + this.getFileName()) : ''; var lineNumber = _isNumber(this.getLineNumber()) ? (':' + this.getLineNumber()) : ''; var columnNumber = _isNumber(this.getColumnNumber()) ? (':' + this.getColumnNumber()) : ''; return functionName + args + fileName + lineNumber + columnNumber; } }; for (var i = 0; i < booleanProps.length; i++) { StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]); StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function(p) { return function(v) { this[p] = Boolean(v); }; })(booleanProps[i]); } for (var j = 0; j < numericProps.length; j++) { StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]); StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function(p) { return function(v) { if (!_isNumber(v)) { throw new TypeError(p + ' must be a Number'); } this[p] = Number(v); }; })(numericProps[j]); } for (var k = 0; k < stringProps.length; k++) { StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]); StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function(p) { return function(v) { this[p] = String(v); }; })(stringProps[k]); } return StackFrame; })); }); var errorStackParser = createCommonjsModule(function (module, exports) { (function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { undefined('error-stack-parser', ['stackframe'], factory); } else { module.exports = factory(stackframe); } }(commonjsGlobal, function ErrorStackParser(StackFrame) { 'use strict'; var FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+\:\d+/; var CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+\:\d+|\(native\))/m; var SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code\])?$/; return { /** * Given an Error object, extract the most information from it. * * @param {Error} error object * @return {Array} of StackFrames */ parse: function ErrorStackParser$$parse(error) { if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') { return this.parseOpera(error); } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) { return this.parseV8OrIE(error); } else if (error.stack) { return this.parseFFOrSafari(error); } else { throw new Error('Cannot parse given Error object'); } }, // Separate line and column numbers from a string of the form: (URI:Line:Column) extractLocation: function ErrorStackParser$$extractLocation(urlLike) { // Fail-fast but return locations like "(native)" if (urlLike.indexOf(':') === -1) { return [urlLike]; } var regExp = /(.+?)(?:\:(\d+))?(?:\:(\d+))?$/; var parts = regExp.exec(urlLike.replace(/[\(\)]/g, '')); return [parts[1], parts[2] || undefined, parts[3] || undefined]; }, parseV8OrIE: function ErrorStackParser$$parseV8OrIE(error) { var filtered = error.stack.split('\n').filter(function(line) { return !!line.match(CHROME_IE_STACK_REGEXP); }, this); return filtered.map(function(line) { if (line.indexOf('(eval ') > -1) { // Throw away eval information until we implement stacktrace.js/stackframe#8 line = line.replace(/eval code/g, 'eval').replace(/(\(eval at [^\()]*)|(\)\,.*$)/g, ''); } var tokens = line.replace(/^\s+/, '').replace(/\(eval code/g, '(').split(/\s+/).slice(1); var locationParts = this.extractLocation(tokens.pop()); var functionName = tokens.join(' ') || undefined; var fileName = ['eval', ''].indexOf(locationParts[0]) > -1 ? undefined : locationParts[0]; return new StackFrame({ functionName: functionName, fileName: fileName, lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); }, this); }, parseFFOrSafari: function ErrorStackParser$$parseFFOrSafari(error) { var filtered = error.stack.split('\n').filter(function(line) { return !line.match(SAFARI_NATIVE_CODE_REGEXP); }, this); return filtered.map(function(line) { // Throw away eval information until we implement stacktrace.js/stackframe#8 if (line.indexOf(' > eval') > -1) { line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval\:\d+\:\d+/g, ':$1'); } if (line.indexOf('@') === -1 && line.indexOf(':') === -1) { // Safari eval frames only have function names and nothing else return new StackFrame({ functionName: line }); } else { var tokens = line.split('@'); var locationParts = this.extractLocation(tokens.pop()); var functionName = tokens.join('@') || undefined; return new StackFrame({ functionName: functionName, fileName: locationParts[0], lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); } }, this); }, parseOpera: function ErrorStackParser$$parseOpera(e) { if (!e.stacktrace || (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length)) { return this.parseOpera9(e); } else if (!e.stack) { return this.parseOpera10(e); } else { return this.parseOpera11(e); } }, parseOpera9: function ErrorStackParser$$parseOpera9(e) { var lineRE = /Line (\d+).*script (?:in )?(\S+)/i; var lines = e.message.split('\n'); var result = []; for (var i = 2, len = lines.length; i < len; i += 2) { var match = lineRE.exec(lines[i]); if (match) { result.push(new StackFrame({ fileName: match[2], lineNumber: match[1], source: lines[i] })); } } return result; }, parseOpera10: function ErrorStackParser$$parseOpera10(e) { var lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i; var lines = e.stacktrace.split('\n'); var result = []; for (var i = 0, len = lines.length; i < len; i += 2) { var match = lineRE.exec(lines[i]); if (match) { result.push( new StackFrame({ functionName: match[3] || undefined, fileName: match[2], lineNumber: match[1], source: lines[i] }) ); } } return result; }, // Opera 10.65+ Error.stack very similar to FF/Safari parseOpera11: function ErrorStackParser$$parseOpera11(error) { var filtered = error.stack.split('\n').filter(function(line) { return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/); }, this); return filtered.map(function(line) { var tokens = line.split('@'); var locationParts = this.extractLocation(tokens.pop()); var functionCall = (tokens.shift() || ''); var functionName = functionCall .replace(//, '$2') .replace(/\([^\)]*\)/g, '') || undefined; var argsRaw; if (functionCall.match(/\(([^\)]*)\)/)) { argsRaw = functionCall.replace(/^[^\(]+\(([^\)]*)\)$/, '$1'); } var args = (argsRaw === undefined || argsRaw === '[arguments not available]') ? undefined : argsRaw.split(','); return new StackFrame({ functionName: functionName, args: args, fileName: locationParts[0], lineNumber: locationParts[1], columnNumber: locationParts[2], source: line }); }, this); } }; })); }); var stackGenerator = createCommonjsModule(function (module, exports) { (function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { undefined('stack-generator', ['stackframe'], factory); } else { module.exports = factory(stackframe); } }(commonjsGlobal, function(StackFrame) { return { backtrace: function StackGenerator$$backtrace(opts) { var stack = []; var maxStackSize = 10; if (typeof opts === 'object' && typeof opts.maxStackSize === 'number') { maxStackSize = opts.maxStackSize; } var curr = arguments.callee; while (curr && stack.length < maxStackSize) { // Allow V8 optimizations var args = new Array(curr['arguments'].length); for (var i = 0; i < args.length; ++i) { args[i] = curr['arguments'][i]; } if (/function(?:\s+([\w$]+))+\s*\(/.test(curr.toString())) { stack.push(new StackFrame({functionName: RegExp.$1 || undefined, args: args})); } else { stack.push(new StackFrame({args: args})); } try { curr = curr.caller; } catch (e) { break; } } return stack; } }; })); }); var util = createCommonjsModule(function (module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ /** * This is a helper function for getting values from parameter/options * objects. * * @param args The object we are extracting values from * @param name The name of the property we are getting. * @param defaultValue An optional value to return if the property is missing * from the object. If this is not specified and the property is missing, an * error will be thrown. */ function getArg(aArgs, aName, aDefaultValue) { if (aName in aArgs) { return aArgs[aName]; } else if (arguments.length === 3) { return aDefaultValue; } else { throw new Error('"' + aName + '" is a required argument.'); } } exports.getArg = getArg; var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; var dataUrlRegexp = /^data:.+\,.+$/; function urlParse(aUrl) { var match = aUrl.match(urlRegexp); if (!match) { return null; } return { scheme: match[1], auth: match[2], host: match[3], port: match[4], path: match[5] }; } exports.urlParse = urlParse; function urlGenerate(aParsedUrl) { var url = ''; if (aParsedUrl.scheme) { url += aParsedUrl.scheme + ':'; } url += '//'; if (aParsedUrl.auth) { url += aParsedUrl.auth + '@'; } if (aParsedUrl.host) { url += aParsedUrl.host; } if (aParsedUrl.port) { url += ":" + aParsedUrl.port; } if (aParsedUrl.path) { url += aParsedUrl.path; } return url; } exports.urlGenerate = urlGenerate; /** * Normalizes a path, or the path portion of a URL: * * - Replaces consecutive slashes with one slash. * - Removes unnecessary '.' parts. * - Removes unnecessary '/..' parts. * * Based on code in the Node.js 'path' core module. * * @param aPath The path or url to normalize. */ function normalize(aPath) { var path = aPath; var url = urlParse(aPath); if (url) { if (!url.path) { return aPath; } path = url.path; } var isAbsolute = exports.isAbsolute(path); var parts = path.split(/\/+/); for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { part = parts[i]; if (part === '.') { parts.splice(i, 1); } else if (part === '..') { up++; } else if (up > 0) { if (part === '') { // The first part is blank if the path is absolute. Trying to go // above the root is a no-op. Therefore we can remove all '..' parts // directly after the root. parts.splice(i + 1, up); up = 0; } else { parts.splice(i, 2); up--; } } } path = parts.join('/'); if (path === '') { path = isAbsolute ? '/' : '.'; } if (url) { url.path = path; return urlGenerate(url); } return path; } exports.normalize = normalize; /** * Joins two paths/URLs. * * @param aRoot The root path or URL. * @param aPath The path or URL to be joined with the root. * * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a * scheme-relative URL: Then the scheme of aRoot, if any, is prepended * first. * - Otherwise aPath is a path. If aRoot is a URL, then its path portion * is updated with the result and aRoot is returned. Otherwise the result * is returned. * - If aPath is absolute, the result is aPath. * - Otherwise the two paths are joined with a slash. * - Joining for example 'http://' and 'www.example.com' is also supported. */ function join(aRoot, aPath) { if (aRoot === "") { aRoot = "."; } if (aPath === "") { aPath = "."; } var aPathUrl = urlParse(aPath); var aRootUrl = urlParse(aRoot); if (aRootUrl) { aRoot = aRootUrl.path || '/'; } // `join(foo, '//www.example.org')` if (aPathUrl && !aPathUrl.scheme) { if (aRootUrl) { aPathUrl.scheme = aRootUrl.scheme; } return urlGenerate(aPathUrl); } if (aPathUrl || aPath.match(dataUrlRegexp)) { return aPath; } // `join('http://', 'www.example.com')` if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { aRootUrl.host = aPath; return urlGenerate(aRootUrl); } var joined = aPath.charAt(0) === '/' ? aPath : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); if (aRootUrl) { aRootUrl.path = joined; return urlGenerate(aRootUrl); } return joined; } exports.join = join; exports.isAbsolute = function (aPath) { return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); }; /** * Make a path relative to a URL or another path. * * @param aRoot The root path or URL. * @param aPath The path or URL to be made relative to aRoot. */ function relative(aRoot, aPath) { if (aRoot === "") { aRoot = "."; } aRoot = aRoot.replace(/\/$/, ''); // It is possible for the path to be above the root. In this case, simply // checking whether the root is a prefix of the path won't work. Instead, we // need to remove components from the root one by one, until either we find // a prefix that fits, or we run out of components to remove. var level = 0; while (aPath.indexOf(aRoot + '/') !== 0) { var index = aRoot.lastIndexOf("/"); if (index < 0) { return aPath; } // If the only part of the root that is left is the scheme (i.e. http://, // file:///, etc.), one or more slashes (/), or simply nothing at all, we // have exhausted all components, so the path is not relative to the root. aRoot = aRoot.slice(0, index); if (aRoot.match(/^([^\/]+:\/)?\/*$/)) { return aPath; } ++level; } // Make sure we add a "../" for each component we removed from the root. return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1); } exports.relative = relative; var supportsNullProto = (function () { var obj = Object.create(null); return !('__proto__' in obj); }()); function identity (s) { return s; } /** * Because behavior goes wacky when you set `__proto__` on objects, we * have to prefix all the strings in our set with an arbitrary character. * * See https://github.com/mozilla/source-map/pull/31 and * https://github.com/mozilla/source-map/issues/30 * * @param String aStr */ function toSetString(aStr) { if (isProtoString(aStr)) { return '$' + aStr; } return aStr; } exports.toSetString = supportsNullProto ? identity : toSetString; function fromSetString(aStr) { if (isProtoString(aStr)) { return aStr.slice(1); } return aStr; } exports.fromSetString = supportsNullProto ? identity : fromSetString; function isProtoString(s) { if (!s) { return false; } var length = s.length; if (length < 9 /* "__proto__".length */) { return false; } if (s.charCodeAt(length - 1) !== 95 /* '_' */ || s.charCodeAt(length - 2) !== 95 /* '_' */ || s.charCodeAt(length - 3) !== 111 /* 'o' */ || s.charCodeAt(length - 4) !== 116 /* 't' */ || s.charCodeAt(length - 5) !== 111 /* 'o' */ || s.charCodeAt(length - 6) !== 114 /* 'r' */ || s.charCodeAt(length - 7) !== 112 /* 'p' */ || s.charCodeAt(length - 8) !== 95 /* '_' */ || s.charCodeAt(length - 9) !== 95 /* '_' */) { return false; } for (var i = length - 10; i >= 0; i--) { if (s.charCodeAt(i) !== 36 /* '$' */) { return false; } } return true; } /** * Comparator between two mappings where the original positions are compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same original source/line/column, but different generated * line and column the same. Useful when searching for a mapping with a * stubbed out mapping. */ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { var cmp = mappingA.source - mappingB.source; if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0 || onlyCompareOriginal) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } return mappingA.name - mappingB.name; } exports.compareByOriginalPositions = compareByOriginalPositions; /** * Comparator between two mappings with deflated source and name indices where * the generated positions are compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same generated line and column, but different * source/name/original line and column the same. Useful when searching for a * mapping with a stubbed out mapping. */ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { var cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0 || onlyCompareGenerated) { return cmp; } cmp = mappingA.source - mappingB.source; if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0) { return cmp; } return mappingA.name - mappingB.name; } exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; function strcmp(aStr1, aStr2) { if (aStr1 === aStr2) { return 0; } if (aStr1 > aStr2) { return 1; } return -1; } /** * Comparator between two mappings with inflated source and name strings where * the generated positions are compared. */ function compareByGeneratedPositionsInflated(mappingA, mappingB) { var cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0) { return cmp; } cmp = strcmp(mappingA.source, mappingB.source); if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0) { return cmp; } return strcmp(mappingA.name, mappingB.name); } exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; }); var binarySearch = createCommonjsModule(function (module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ exports.GREATEST_LOWER_BOUND = 1; exports.LEAST_UPPER_BOUND = 2; /** * Recursive implementation of binary search. * * @param aLow Indices here and lower do not contain the needle. * @param aHigh Indices here and higher do not contain the needle. * @param aNeedle The element being searched for. * @param aHaystack The non-empty array being searched. * @param aCompare Function which takes two elements and returns -1, 0, or 1. * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the * closest element that is smaller than or greater than the one we are * searching for, respectively, if the exact element cannot be found. */ function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) { // This function terminates when one of the following is true: // // 1. We find the exact element we are looking for. // // 2. We did not find the exact element, but we can return the index of // the next-closest element. // // 3. We did not find the exact element, and there is no next-closest // element than the one we are searching for, so we return -1. var mid = Math.floor((aHigh - aLow) / 2) + aLow; var cmp = aCompare(aNeedle, aHaystack[mid], true); if (cmp === 0) { // Found the element we are looking for. return mid; } else if (cmp > 0) { // Our needle is greater than aHaystack[mid]. if (aHigh - mid > 1) { // The element is in the upper half. return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias); } // The exact needle element was not found in this haystack. Determine if // we are in termination case (3) or (2) and return the appropriate thing. if (aBias == exports.LEAST_UPPER_BOUND) { return aHigh < aHaystack.length ? aHigh : -1; } else { return mid; } } else { // Our needle is less than aHaystack[mid]. if (mid - aLow > 1) { // The element is in the lower half. return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias); } // we are in termination case (3) or (2) and return the appropriate thing. if (aBias == exports.LEAST_UPPER_BOUND) { return mid; } else { return aLow < 0 ? -1 : aLow; } } } /** * This is an implementation of binary search which will always try and return * the index of the closest element if there is no exact hit. This is because * mappings between original and generated line/col pairs are single points, * and there is an implicit region between each of them, so a miss just means * that you aren't on the very start of a region. * * @param aNeedle The element you are looking for. * @param aHaystack The array that is being searched. * @param aCompare A function which takes the needle and an element in the * array and returns -1, 0, or 1 depending on whether the needle is less * than, equal to, or greater than the element, respectively. * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the * closest element that is smaller than or greater than the one we are * searching for, respectively, if the exact element cannot be found. * Defaults to 'binarySearch.GREATEST_LOWER_BOUND'. */ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { if (aHaystack.length === 0) { return -1; } var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare, aBias || exports.GREATEST_LOWER_BOUND); if (index < 0) { return -1; } // We have found either the exact element, or the next-closest element than // the one we are searching for. However, there may be more than one such // element. Make sure we always return the smallest of these. while (index - 1 >= 0) { if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) { break; } --index; } return index; }; }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ var has = Object.prototype.hasOwnProperty; /** * A data structure which is a combination of an array and a set. Adding a new * member is O(1), testing for membership is O(1), and finding the index of an * element is O(1). Removing elements from the set is not supported. Only * strings are supported for membership. */ function ArraySet$1() { this._array = []; this._set = Object.create(null); } /** * Static method for creating ArraySet instances from an existing array. */ ArraySet$1.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { var set = new ArraySet$1(); for (var i = 0, len = aArray.length; i < len; i++) { set.add(aArray[i], aAllowDuplicates); } return set; }; /** * Return how many unique items are in this ArraySet. If duplicates have been * added, than those do not count towards the size. * * @returns Number */ ArraySet$1.prototype.size = function ArraySet_size() { return Object.getOwnPropertyNames(this._set).length; }; /** * Add the given string to this set. * * @param String aStr */ ArraySet$1.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { var sStr = util.toSetString(aStr); var isDuplicate = has.call(this._set, sStr); var idx = this._array.length; if (!isDuplicate || aAllowDuplicates) { this._array.push(aStr); } if (!isDuplicate) { this._set[sStr] = idx; } }; /** * Is the given string a member of this set? * * @param String aStr */ ArraySet$1.prototype.has = function ArraySet_has(aStr) { var sStr = util.toSetString(aStr); return has.call(this._set, sStr); }; /** * What is the index of the given string in the array? * * @param String aStr */ ArraySet$1.prototype.indexOf = function ArraySet_indexOf(aStr) { var sStr = util.toSetString(aStr); if (has.call(this._set, sStr)) { return this._set[sStr]; } throw new Error('"' + aStr + '" is not in the set.'); }; /** * What is the element at the given index? * * @param Number aIdx */ ArraySet$1.prototype.at = function ArraySet_at(aIdx) { if (aIdx >= 0 && aIdx < this._array.length) { return this._array[aIdx]; } throw new Error('No element indexed by ' + aIdx); }; /** * Returns the array representation of this set (which has the proper indices * indicated by indexOf). Note that this is a copy of the internal array used * for storing the members so that no one can mess with internal state. */ ArraySet$1.prototype.toArray = function ArraySet_toArray() { return this._array.slice(); }; var ArraySet_1 = ArraySet$1; var arraySet = { ArraySet: ArraySet_1 }; /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); /** * Encode an integer in the range of 0 to 63 to a single base 64 digit. */ var encode$2 = function (number) { if (0 <= number && number < intToCharMap.length) { return intToCharMap[number]; } throw new TypeError("Must be between 0 and 63: " + number); }; /** * Decode a single base 64 character code digit to an integer. Returns -1 on * failure. */ var decode$1 = function (charCode) { var bigA = 65; // 'A' var bigZ = 90; // 'Z' var littleA = 97; // 'a' var littleZ = 122; // 'z' var zero = 48; // '0' var nine = 57; // '9' var plus = 43; // '+' var slash = 47; // '/' var littleOffset = 26; var numberOffset = 52; // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ if (bigA <= charCode && charCode <= bigZ) { return (charCode - bigA); } // 26 - 51: abcdefghijklmnopqrstuvwxyz if (littleA <= charCode && charCode <= littleZ) { return (charCode - littleA + littleOffset); } // 52 - 61: 0123456789 if (zero <= charCode && charCode <= nine) { return (charCode - zero + numberOffset); } // 62: + if (charCode == plus) { return 62; } // 63: / if (charCode == slash) { return 63; } // Invalid base64 digit. return -1; }; var base64 = { encode: encode$2, decode: decode$1 }; /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause * * Based on the Base 64 VLQ implementation in Closure Compiler: * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java * * Copyright 2011 The Closure Compiler Authors. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, // the next four bits are the actual value, and the 6th bit is the // continuation bit. The continuation bit tells us whether there are more // digits in this value following this digit. // // Continuation // | Sign // | | // V V // 101011 var VLQ_BASE_SHIFT = 5; // binary: 100000 var VLQ_BASE = 1 << VLQ_BASE_SHIFT; // binary: 011111 var VLQ_BASE_MASK = VLQ_BASE - 1; // binary: 100000 var VLQ_CONTINUATION_BIT = VLQ_BASE; /** * Converts from a two-complement value to a value where the sign bit is * placed in the least significant bit. For example, as decimals: * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) */ function toVLQSigned(aValue) { return aValue < 0 ? ((-aValue) << 1) + 1 : (aValue << 1) + 0; } /** * Converts to a two-complement value from a value where the sign bit is * placed in the least significant bit. For example, as decimals: * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 */ function fromVLQSigned(aValue) { var isNegative = (aValue & 1) === 1; var shifted = aValue >> 1; return isNegative ? -shifted : shifted; } /** * Returns the base 64 VLQ encoded value. */ var encode$1 = function base64VLQ_encode(aValue) { var encoded = ""; var digit; var vlq = toVLQSigned(aValue); do { digit = vlq & VLQ_BASE_MASK; vlq >>>= VLQ_BASE_SHIFT; if (vlq > 0) { // There are still more digits in this value, so we must make sure the // continuation bit is marked. digit |= VLQ_CONTINUATION_BIT; } encoded += base64.encode(digit); } while (vlq > 0); return encoded; }; /** * Decodes the next base 64 VLQ value from the given string and returns the * value and the rest of the string via the out parameter. */ var decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { var strLen = aStr.length; var result = 0; var shift = 0; var continuation, digit; do { if (aIndex >= strLen) { throw new Error("Expected more digits in base 64 VLQ value."); } digit = base64.decode(aStr.charCodeAt(aIndex++)); if (digit === -1) { throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); } continuation = !!(digit & VLQ_CONTINUATION_BIT); digit &= VLQ_BASE_MASK; result = result + (digit << shift); shift += VLQ_BASE_SHIFT; } while (continuation); aOutParam.value = fromVLQSigned(result); aOutParam.rest = aIndex; }; var base64Vlq = { encode: encode$1, decode: decode }; /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ // It turns out that some (most?) JavaScript engines don't self-host // `Array.prototype.sort`. This makes sense because C++ will likely remain // faster than JS when doing raw CPU-intensive sorting. However, when using a // custom comparator function, calling back and forth between the VM's C++ and // JIT'd JS is rather slow *and* loses JIT type information, resulting in // worse generated code for the comparator function than would be optimal. In // fact, when sorting with a comparator, these costs outweigh the benefits of // sorting in C++. By using our own JS-implemented Quick Sort (below), we get // a ~3500ms mean speed-up in `bench/bench.html`. /** * Swap the elements indexed by `x` and `y` in the array `ary`. * * @param {Array} ary * The array. * @param {Number} x * The index of the first item. * @param {Number} y * The index of the second item. */ function swap(ary, x, y) { var temp = ary[x]; ary[x] = ary[y]; ary[y] = temp; } /** * Returns a random integer within the range `low .. high` inclusive. * * @param {Number} low * The lower bound on the range. * @param {Number} high * The upper bound on the range. */ function randomIntInRange(low, high) { return Math.round(low + (Math.random() * (high - low))); } /** * The Quick Sort algorithm. * * @param {Array} ary * An array to sort. * @param {function} comparator * Function to use to compare two items. * @param {Number} p * Start index of the array * @param {Number} r * End index of the array */ function doQuickSort(ary, comparator, p, r) { // If our lower bound is less than our upper bound, we (1) partition the // array into two pieces and (2) recurse on each half. If it is not, this is // the empty array and our base case. if (p < r) { // (1) Partitioning. // // The partitioning chooses a pivot between `p` and `r` and moves all // elements that are less than or equal to the pivot to the before it, and // all the elements that are greater than it after it. The effect is that // once partition is done, the pivot is in the exact place it will be when // the array is put in sorted order, and it will not need to be moved // again. This runs in O(n) time. // Always choose a random pivot so that an input array which is reverse // sorted does not cause O(n^2) running time. var pivotIndex = randomIntInRange(p, r); var i = p - 1; swap(ary, pivotIndex, r); var pivot = ary[r]; // Immediately after `j` is incremented in this loop, the following hold // true: // // * Every element in `ary[p .. i]` is less than or equal to the pivot. // // * Every element in `ary[i+1 .. j-1]` is greater than the pivot. for (var j = p; j < r; j++) { if (comparator(ary[j], pivot) <= 0) { i += 1; swap(ary, i, j); } } swap(ary, i + 1, j); var q = i + 1; // (2) Recurse on each half. doQuickSort(ary, comparator, p, q - 1); doQuickSort(ary, comparator, q + 1, r); } } /** * Sort the given array in-place with the given comparator function. * * @param {Array} ary * An array to sort. * @param {function} comparator * Function to use to compare two items. */ var quickSort_1 = function (ary, comparator) { doQuickSort(ary, comparator, 0, ary.length - 1); }; var quickSort$1 = { quickSort: quickSort_1 }; /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ var ArraySet = arraySet.ArraySet; var quickSort = quickSort$1.quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; if (typeof aSourceMap === 'string') { sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } return sourceMap.sections != null ? new IndexedSourceMapConsumer(sourceMap) : new BasicSourceMapConsumer(sourceMap); } SourceMapConsumer.fromSourceMap = function(aSourceMap) { return BasicSourceMapConsumer.fromSourceMap(aSourceMap); }; /** * The version of the source mapping spec that we are consuming. */ SourceMapConsumer.prototype._version = 3; // `__generatedMappings` and `__originalMappings` are arrays that hold the // parsed mapping coordinates from the source map's "mappings" attribute. They // are lazily instantiated, accessed via the `_generatedMappings` and // `_originalMappings` getters respectively, and we only parse the mappings // and create these arrays once queried for a source location. We jump through // these hoops because there can be many thousands of mappings, and parsing // them is expensive, so we only want to do it if we must. // // Each object in the arrays is of the form: // // { // generatedLine: The line number in the generated code, // generatedColumn: The column number in the generated code, // source: The path to the original source file that generated this // chunk of code, // originalLine: The line number in the original source that // corresponds to this chunk of generated code, // originalColumn: The column number in the original source that // corresponds to this chunk of generated code, // name: The name of the original symbol which generated this chunk of // code. // } // // All properties except for `generatedLine` and `generatedColumn` can be // `null`. // // `_generatedMappings` is ordered by the generated positions. // // `_originalMappings` is ordered by the original positions. SourceMapConsumer.prototype.__generatedMappings = null; Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { get: function () { if (!this.__generatedMappings) { this._parseMappings(this._mappings, this.sourceRoot); } return this.__generatedMappings; } }); SourceMapConsumer.prototype.__originalMappings = null; Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { get: function () { if (!this.__originalMappings) { this._parseMappings(this._mappings, this.sourceRoot); } return this.__originalMappings; } }); SourceMapConsumer.prototype._charIsMappingSeparator = function SourceMapConsumer_charIsMappingSeparator(aStr, index) { var c = aStr.charAt(index); return c === ";" || c === ","; }; /** * Parse the mappings in a string in to a data structure which we can easily * query (the ordered arrays in the `this.__generatedMappings` and * `this.__originalMappings` properties). */ SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { throw new Error("Subclasses must implement _parseMappings"); }; SourceMapConsumer.GENERATED_ORDER = 1; SourceMapConsumer.ORIGINAL_ORDER = 2; SourceMapConsumer.GREATEST_LOWER_BOUND = 1; SourceMapConsumer.LEAST_UPPER_BOUND = 2; /** * Iterate over each mapping between an original source/line/column and a * generated line/column in this source map. * * @param Function aCallback * The function that is called with each mapping. * @param Object aContext * Optional. If specified, this object will be the value of `this` every * time that `aCallback` is called. * @param aOrder * Either `SourceMapConsumer.GENERATED_ORDER` or * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to * iterate over the mappings sorted by the generated file's line/column * order or the original's source/line/column order, respectively. Defaults to * `SourceMapConsumer.GENERATED_ORDER`. */ SourceMapConsumer.prototype.eachMapping = function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { var context = aContext || null; var order = aOrder || SourceMapConsumer.GENERATED_ORDER; var mappings; switch (order) { case SourceMapConsumer.GENERATED_ORDER: mappings = this._generatedMappings; break; case SourceMapConsumer.ORIGINAL_ORDER: mappings = this._originalMappings; break; default: throw new Error("Unknown order of iteration."); } var sourceRoot = this.sourceRoot; mappings.map(function (mapping) { var source = mapping.source === null ? null : this._sources.at(mapping.source); if (source != null && sourceRoot != null) { source = util.join(sourceRoot, source); } return { source: source, generatedLine: mapping.generatedLine, generatedColumn: mapping.generatedColumn, originalLine: mapping.originalLine, originalColumn: mapping.originalColumn, name: mapping.name === null ? null : this._names.at(mapping.name) }; }, this).forEach(aCallback, context); }; /** * Returns all generated line and column information for the original source, * line, and column provided. If no column is provided, returns all mappings * corresponding to a either the line we are searching for or the next * closest line that has any mappings. Otherwise, returns all mappings * corresponding to the given line and either the column we are searching for * or the next closest column that has any offsets. * * The only argument is an object with the following properties: * * - source: The filename of the original source. * - line: The line number in the original source. * - column: Optional. the column number in the original source. * * and an array of objects is returned, each with the following properties: * * - line: The line number in the generated source, or null. * - column: The column number in the generated source, or null. */ SourceMapConsumer.prototype.allGeneratedPositionsFor = function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { var line = util.getArg(aArgs, 'line'); // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping // returns the index of the closest mapping less than the needle. By // setting needle.originalColumn to 0, we thus find the last mapping for // the given line, provided such a mapping exists. var needle = { source: util.getArg(aArgs, 'source'), originalLine: line, originalColumn: util.getArg(aArgs, 'column', 0) }; if (this.sourceRoot != null) { needle.source = util.relative(this.sourceRoot, needle.source); } if (!this._sources.has(needle.source)) { return []; } needle.source = this._sources.indexOf(needle.source); var mappings = []; var index = this._findMapping(needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, binarySearch.LEAST_UPPER_BOUND); if (index >= 0) { var mapping = this._originalMappings[index]; if (aArgs.column === undefined) { var originalLine = mapping.originalLine; // Iterate until either we run out of mappings, or we run into // a mapping for a different line than the one we found. Since // mappings are sorted, this is guaranteed to find all mappings for // the line we found. while (mapping && mapping.originalLine === originalLine) { mappings.push({ line: util.getArg(mapping, 'generatedLine', null), column: util.getArg(mapping, 'generatedColumn', null), lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) }); mapping = this._originalMappings[++index]; } } else { var originalColumn = mapping.originalColumn; // Iterate until either we run out of mappings, or we run into // a mapping for a different line than the one we were searching for. // Since mappings are sorted, this is guaranteed to find all mappings for // the line we are searching for. while (mapping && mapping.originalLine === line && mapping.originalColumn == originalColumn) { mappings.push({ line: util.getArg(mapping, 'generatedLine', null), column: util.getArg(mapping, 'generatedColumn', null), lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) }); mapping = this._originalMappings[++index]; } } } return mappings; }; var SourceMapConsumer_1 = SourceMapConsumer; /** * A BasicSourceMapConsumer instance represents a parsed source map which we can * query for information about the original file positions by giving it a file * position in the generated source. * * The only parameter is the raw source map (either as a JSON string, or * already parsed to an object). According to the spec, source maps have the * following attributes: * * - version: Which version of the source map spec this map is following. * - sources: An array of URLs to the original source files. * - names: An array of identifiers which can be referrenced by individual mappings. * - sourceRoot: Optional. The URL root from which all sources are relative. * - sourcesContent: Optional. An array of contents of the original source files. * - mappings: A string of base64 VLQs which contain the actual mappings. * - file: Optional. The generated file this source map is associated with. * * Here is an example source map, taken from the source map spec[0]: * * { * version : 3, * file: "out.js", * sourceRoot : "", * sources: ["foo.js", "bar.js"], * names: ["src", "maps", "are", "fun"], * mappings: "AA,AB;;ABCDE;" * } * * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# */ function BasicSourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; if (typeof aSourceMap === 'string') { sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } var version = util.getArg(sourceMap, 'version'); var sources = util.getArg(sourceMap, 'sources'); // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which // requires the array) to play nice here. var names = util.getArg(sourceMap, 'names', []); var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); var mappings = util.getArg(sourceMap, 'mappings'); var file = util.getArg(sourceMap, 'file', null); // Once again, Sass deviates from the spec and supplies the version as a // string rather than a number, so we use loose equality checking here. if (version != this._version) { throw new Error('Unsupported version: ' + version); } sources = sources .map(String) // Some source maps produce relative source paths like "./foo.js" instead of // "foo.js". Normalize these first so that future comparisons will succeed. // See bugzil.la/1090768. .map(util.normalize) // Always ensure that absolute sources are internally stored relative to // the source root, if the source root is absolute. Not doing this would // be particularly problematic when the source root is a prefix of the // source (valid, but why??). See github issue #199 and bugzil.la/1188982. .map(function (source) { return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) ? util.relative(sourceRoot, source) : source; }); // Pass `true` below to allow duplicate names and sources. While source maps // are intended to be compressed and deduplicated, the TypeScript compiler // sometimes generates source maps with duplicates in them. See Github issue // #72 and bugzil.la/889492. this._names = ArraySet.fromArray(names.map(String), true); this._sources = ArraySet.fromArray(sources, true); this.sourceRoot = sourceRoot; this.sourcesContent = sourcesContent; this._mappings = mappings; this.file = file; } BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; /** * Create a BasicSourceMapConsumer from a SourceMapGenerator. * * @param SourceMapGenerator aSourceMap * The source map that will be consumed. * @returns BasicSourceMapConsumer */ BasicSourceMapConsumer.fromSourceMap = function SourceMapConsumer_fromSourceMap(aSourceMap) { var smc = Object.create(BasicSourceMapConsumer.prototype); var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); smc.sourceRoot = aSourceMap._sourceRoot; smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), smc.sourceRoot); smc.file = aSourceMap._file; // Because we are modifying the entries (by converting string sources and // names to indices into the sources and names ArraySets), we have to make // a copy of the entry or else bad things happen. Shared mutable state // strikes again! See github issue #191. var generatedMappings = aSourceMap._mappings.toArray().slice(); var destGeneratedMappings = smc.__generatedMappings = []; var destOriginalMappings = smc.__originalMappings = []; for (var i = 0, length = generatedMappings.length; i < length; i++) { var srcMapping = generatedMappings[i]; var destMapping = new Mapping; destMapping.generatedLine = srcMapping.generatedLine; destMapping.generatedColumn = srcMapping.generatedColumn; if (srcMapping.source) { destMapping.source = sources.indexOf(srcMapping.source); destMapping.originalLine = srcMapping.originalLine; destMapping.originalColumn = srcMapping.originalColumn; if (srcMapping.name) { destMapping.name = names.indexOf(srcMapping.name); } destOriginalMappings.push(destMapping); } destGeneratedMappings.push(destMapping); } quickSort(smc.__originalMappings, util.compareByOriginalPositions); return smc; }; /** * The version of the source mapping spec that we are consuming. */ BasicSourceMapConsumer.prototype._version = 3; /** * The list of original sources. */ Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', { get: function () { return this._sources.toArray().map(function (s) { return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s; }, this); } }); /** * Provide the JIT with a nice shape / hidden class. */ function Mapping() { this.generatedLine = 0; this.generatedColumn = 0; this.source = null; this.originalLine = null; this.originalColumn = null; this.name = null; } /** * Parse the mappings in a string in to a data structure which we can easily * query (the ordered arrays in the `this.__generatedMappings` and * `this.__originalMappings` properties). */ BasicSourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { var generatedLine = 1; var previousGeneratedColumn = 0; var previousOriginalLine = 0; var previousOriginalColumn = 0; var previousSource = 0; var previousName = 0; var length = aStr.length; var index = 0; var cachedSegments = {}; var temp = {}; var originalMappings = []; var generatedMappings = []; var mapping, str, segment, end, value; while (index < length) { if (aStr.charAt(index) === ';') { generatedLine++; index++; previousGeneratedColumn = 0; } else if (aStr.charAt(index) === ',') { index++; } else { mapping = new Mapping(); mapping.generatedLine = generatedLine; // Because each offset is encoded relative to the previous one, // many segments often have the same encoding. We can exploit this // fact by caching the parsed variable length fields of each segment, // allowing us to avoid a second parse if we encounter the same // segment again. for (end = index; end < length; end++) { if (this._charIsMappingSeparator(aStr, end)) { break; } } str = aStr.slice(index, end); segment = cachedSegments[str]; if (segment) { index += str.length; } else { segment = []; while (index < end) { base64Vlq.decode(aStr, index, temp); value = temp.value; index = temp.rest; segment.push(value); } if (segment.length === 2) { throw new Error('Found a source, but no line and column'); } if (segment.length === 3) { throw new Error('Found a source and line, but no column'); } cachedSegments[str] = segment; } // Generated column. mapping.generatedColumn = previousGeneratedColumn + segment[0]; previousGeneratedColumn = mapping.generatedColumn; if (segment.length > 1) { // Original source. mapping.source = previousSource + segment[1]; previousSource += segment[1]; // Original line. mapping.originalLine = previousOriginalLine + segment[2]; previousOriginalLine = mapping.originalLine; // Lines are stored 0-based mapping.originalLine += 1; // Original column. mapping.originalColumn = previousOriginalColumn + segment[3]; previousOriginalColumn = mapping.originalColumn; if (segment.length > 4) { // Original name. mapping.name = previousName + segment[4]; previousName += segment[4]; } } generatedMappings.push(mapping); if (typeof mapping.originalLine === 'number') { originalMappings.push(mapping); } } } quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); this.__generatedMappings = generatedMappings; quickSort(originalMappings, util.compareByOriginalPositions); this.__originalMappings = originalMappings; }; /** * Find the mapping that best matches the hypothetical "needle" mapping that * we are searching for in the given "haystack" of mappings. */ BasicSourceMapConsumer.prototype._findMapping = function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, aColumnName, aComparator, aBias) { // To return the position we are searching for, we must first find the // mapping for the given position and then return the opposite position it // points to. Because the mappings are sorted, we can use binary search to // find the best mapping. if (aNeedle[aLineName] <= 0) { throw new TypeError('Line must be greater than or equal to 1, got ' + aNeedle[aLineName]); } if (aNeedle[aColumnName] < 0) { throw new TypeError('Column must be greater than or equal to 0, got ' + aNeedle[aColumnName]); } return binarySearch.search(aNeedle, aMappings, aComparator, aBias); }; /** * Compute the last column for each generated mapping. The last column is * inclusive. */ BasicSourceMapConsumer.prototype.computeColumnSpans = function SourceMapConsumer_computeColumnSpans() { for (var index = 0; index < this._generatedMappings.length; ++index) { var mapping = this._generatedMappings[index]; // Mappings do not contain a field for the last generated columnt. We // can come up with an optimistic estimate, however, by assuming that // mappings are contiguous (i.e. given two consecutive mappings, the // first mapping ends where the second one starts). if (index + 1 < this._generatedMappings.length) { var nextMapping = this._generatedMappings[index + 1]; if (mapping.generatedLine === nextMapping.generatedLine) { mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1; continue; } } // The last mapping for each line spans the entire line. mapping.lastGeneratedColumn = Infinity; } }; /** * Returns the original source, line, and column information for the generated * source's line and column positions provided. The only argument is an object * with the following properties: * * - line: The line number in the generated source. * - column: The column number in the generated source. * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the * closest element that is smaller than or greater than the one we are * searching for, respectively, if the exact element cannot be found. * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. * * and an object is returned with the following properties: * * - source: The original source file, or null. * - line: The line number in the original source, or null. * - column: The column number in the original source, or null. * - name: The original identifier, or null. */ BasicSourceMapConsumer.prototype.originalPositionFor = function SourceMapConsumer_originalPositionFor(aArgs) { var needle = { generatedLine: util.getArg(aArgs, 'line'), generatedColumn: util.getArg(aArgs, 'column') }; var index = this._findMapping( needle, this._generatedMappings, "generatedLine", "generatedColumn", util.compareByGeneratedPositionsDeflated, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) ); if (index >= 0) { var mapping = this._generatedMappings[index]; if (mapping.generatedLine === needle.generatedLine) { var source = util.getArg(mapping, 'source', null); if (source !== null) { source = this._sources.at(source); if (this.sourceRoot != null) { source = util.join(this.sourceRoot, source); } } var name = util.getArg(mapping, 'name', null); if (name !== null) { name = this._names.at(name); } return { source: source, line: util.getArg(mapping, 'originalLine', null), column: util.getArg(mapping, 'originalColumn', null), name: name }; } } return { source: null, line: null, column: null, name: null }; }; /** * Return true if we have the source content for every source in the source * map, false otherwise. */ BasicSourceMapConsumer.prototype.hasContentsOfAllSources = function BasicSourceMapConsumer_hasContentsOfAllSources() { if (!this.sourcesContent) { return false; } return this.sourcesContent.length >= this._sources.size() && !this.sourcesContent.some(function (sc) { return sc == null; }); }; /** * Returns the original source content. The only argument is the url of the * original source file. Returns null if no original source content is * available. */ BasicSourceMapConsumer.prototype.sourceContentFor = function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { if (!this.sourcesContent) { return null; } if (this.sourceRoot != null) { aSource = util.relative(this.sourceRoot, aSource); } if (this._sources.has(aSource)) { return this.sourcesContent[this._sources.indexOf(aSource)]; } var url; if (this.sourceRoot != null && (url = util.urlParse(this.sourceRoot))) { // XXX: file:// URIs and absolute paths lead to unexpected behavior for // many users. We can help them out when they expect file:// URIs to // behave like it would if they were running a local HTTP server. See // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); if (url.scheme == "file" && this._sources.has(fileUriAbsPath)) { return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] } if ((!url.path || url.path == "/") && this._sources.has("/" + aSource)) { return this.sourcesContent[this._sources.indexOf("/" + aSource)]; } } // This function is used recursively from // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we // don't want to throw if we can't find the source - we just want to // return null, so we provide a flag to exit gracefully. if (nullOnMissing) { return null; } else { throw new Error('"' + aSource + '" is not in the SourceMap.'); } }; /** * Returns the generated line and column information for the original source, * line, and column positions provided. The only argument is an object with * the following properties: * * - source: The filename of the original source. * - line: The line number in the original source. * - column: The column number in the original source. * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the * closest element that is smaller than or greater than the one we are * searching for, respectively, if the exact element cannot be found. * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. * * and an object is returned with the following properties: * * - line: The line number in the generated source, or null. * - column: The column number in the generated source, or null. */ BasicSourceMapConsumer.prototype.generatedPositionFor = function SourceMapConsumer_generatedPositionFor(aArgs) { var source = util.getArg(aArgs, 'source'); if (this.sourceRoot != null) { source = util.relative(this.sourceRoot, source); } if (!this._sources.has(source)) { return { line: null, column: null, lastColumn: null }; } source = this._sources.indexOf(source); var needle = { source: source, originalLine: util.getArg(aArgs, 'line'), originalColumn: util.getArg(aArgs, 'column') }; var index = this._findMapping( needle, this._originalMappings, "originalLine", "originalColumn", util.compareByOriginalPositions, util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) ); if (index >= 0) { var mapping = this._originalMappings[index]; if (mapping.source === needle.source) { return { line: util.getArg(mapping, 'generatedLine', null), column: util.getArg(mapping, 'generatedColumn', null), lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) }; } } return { line: null, column: null, lastColumn: null }; }; var BasicSourceMapConsumer_1 = BasicSourceMapConsumer; /** * An IndexedSourceMapConsumer instance represents a parsed source map which * we can query for information. It differs from BasicSourceMapConsumer in * that it takes "indexed" source maps (i.e. ones with a "sections" field) as * input. * * The only parameter is a raw source map (either as a JSON string, or already * parsed to an object). According to the spec for indexed source maps, they * have the following attributes: * * - version: Which version of the source map spec this map is following. * - file: Optional. The generated file this source map is associated with. * - sections: A list of section definitions. * * Each value under the "sections" field has two fields: * - offset: The offset into the original specified at which this section * begins to apply, defined as an object with a "line" and "column" * field. * - map: A source map definition. This source map could also be indexed, * but doesn't have to be. * * Instead of the "map" field, it's also possible to have a "url" field * specifying a URL to retrieve a source map from, but that's currently * unsupported. * * Here's an example source map, taken from the source map spec[0], but * modified to omit a section which uses the "url" field. * * { * version : 3, * file: "app.js", * sections: [{ * offset: {line:100, column:10}, * map: { * version : 3, * file: "section.js", * sources: ["foo.js", "bar.js"], * names: ["src", "maps", "are", "fun"], * mappings: "AAAA,E;;ABCDE;" * } * }], * } * * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt */ function IndexedSourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; if (typeof aSourceMap === 'string') { sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } var version = util.getArg(sourceMap, 'version'); var sections = util.getArg(sourceMap, 'sections'); if (version != this._version) { throw new Error('Unsupported version: ' + version); } this._sources = new ArraySet(); this._names = new ArraySet(); var lastOffset = { line: -1, column: 0 }; this._sections = sections.map(function (s) { if (s.url) { // The url field will require support for asynchronicity. // See https://github.com/mozilla/source-map/issues/16 throw new Error('Support for url field in sections not implemented.'); } var offset = util.getArg(s, 'offset'); var offsetLine = util.getArg(offset, 'line'); var offsetColumn = util.getArg(offset, 'column'); if (offsetLine < lastOffset.line || (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { throw new Error('Section offsets must be ordered and non-overlapping.'); } lastOffset = offset; return { generatedOffset: { // The offset fields are 0-based, but we use 1-based indices when // encoding/decoding from VLQ. generatedLine: offsetLine + 1, generatedColumn: offsetColumn + 1 }, consumer: new SourceMapConsumer(util.getArg(s, 'map')) } }); } IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; /** * The version of the source mapping spec that we are consuming. */ IndexedSourceMapConsumer.prototype._version = 3; /** * The list of original sources. */ Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', { get: function () { var sources = []; for (var i = 0; i < this._sections.length; i++) { for (var j = 0; j < this._sections[i].consumer.sources.length; j++) { sources.push(this._sections[i].consumer.sources[j]); } } return sources; } }); /** * Returns the original source, line, and column information for the generated * source's line and column positions provided. The only argument is an object * with the following properties: * * - line: The line number in the generated source. * - column: The column number in the generated source. * * and an object is returned with the following properties: * * - source: The original source file, or null. * - line: The line number in the original source, or null. * - column: The column number in the original source, or null. * - name: The original identifier, or null. */ IndexedSourceMapConsumer.prototype.originalPositionFor = function IndexedSourceMapConsumer_originalPositionFor(aArgs) { var needle = { generatedLine: util.getArg(aArgs, 'line'), generatedColumn: util.getArg(aArgs, 'column') }; // Find the section containing the generated position we're trying to map // to an original position. var sectionIndex = binarySearch.search(needle, this._sections, function(needle, section) { var cmp = needle.generatedLine - section.generatedOffset.generatedLine; if (cmp) { return cmp; } return (needle.generatedColumn - section.generatedOffset.generatedColumn); }); var section = this._sections[sectionIndex]; if (!section) { return { source: null, line: null, column: null, name: null }; } return section.consumer.originalPositionFor({ line: needle.generatedLine - (section.generatedOffset.generatedLine - 1), column: needle.generatedColumn - (section.generatedOffset.generatedLine === needle.generatedLine ? section.generatedOffset.generatedColumn - 1 : 0), bias: aArgs.bias }); }; /** * Return true if we have the source content for every source in the source * map, false otherwise. */ IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = function IndexedSourceMapConsumer_hasContentsOfAllSources() { return this._sections.every(function (s) { return s.consumer.hasContentsOfAllSources(); }); }; /** * Returns the original source content. The only argument is the url of the * original source file. Returns null if no original source content is * available. */ IndexedSourceMapConsumer.prototype.sourceContentFor = function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { for (var i = 0; i < this._sections.length; i++) { var section = this._sections[i]; var content = section.consumer.sourceContentFor(aSource, true); if (content) { return content; } } if (nullOnMissing) { return null; } else { throw new Error('"' + aSource + '" is not in the SourceMap.'); } }; /** * Returns the generated line and column information for the original source, * line, and column positions provided. The only argument is an object with * the following properties: * * - source: The filename of the original source. * - line: The line number in the original source. * - column: The column number in the original source. * * and an object is returned with the following properties: * * - line: The line number in the generated source, or null. * - column: The column number in the generated source, or null. */ IndexedSourceMapConsumer.prototype.generatedPositionFor = function IndexedSourceMapConsumer_generatedPositionFor(aArgs) { for (var i = 0; i < this._sections.length; i++) { var section = this._sections[i]; // Only consider this section if the requested source is in the list of // sources of the consumer. if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) { continue; } var generatedPosition = section.consumer.generatedPositionFor(aArgs); if (generatedPosition) { var ret = { line: generatedPosition.line + (section.generatedOffset.generatedLine - 1), column: generatedPosition.column + (section.generatedOffset.generatedLine === generatedPosition.line ? section.generatedOffset.generatedColumn - 1 : 0) }; return ret; } } return { line: null, column: null }; }; /** * Parse the mappings in a string in to a data structure which we can easily * query (the ordered arrays in the `this.__generatedMappings` and * `this.__originalMappings` properties). */ IndexedSourceMapConsumer.prototype._parseMappings = function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) { this.__generatedMappings = []; this.__originalMappings = []; for (var i = 0; i < this._sections.length; i++) { var section = this._sections[i]; var sectionMappings = section.consumer._generatedMappings; for (var j = 0; j < sectionMappings.length; j++) { var mapping = sectionMappings[j]; var source = section.consumer._sources.at(mapping.source); if (section.consumer.sourceRoot !== null) { source = util.join(section.consumer.sourceRoot, source); } this._sources.add(source); source = this._sources.indexOf(source); var name = section.consumer._names.at(mapping.name); this._names.add(name); name = this._names.indexOf(name); // The mappings coming from the consumer for the section have // generated positions relative to the start of the section, so we // need to offset them to be relative to the start of the concatenated // generated file. var adjustedMapping = { source: source, generatedLine: mapping.generatedLine + (section.generatedOffset.generatedLine - 1), generatedColumn: mapping.generatedColumn + (section.generatedOffset.generatedLine === mapping.generatedLine ? section.generatedOffset.generatedColumn - 1 : 0), originalLine: mapping.originalLine, originalColumn: mapping.originalColumn, name: name }; this.__generatedMappings.push(adjustedMapping); if (typeof adjustedMapping.originalLine === 'number') { this.__originalMappings.push(adjustedMapping); } } } quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); quickSort(this.__originalMappings, util.compareByOriginalPositions); }; var IndexedSourceMapConsumer_1 = IndexedSourceMapConsumer; var sourceMapConsumer = { SourceMapConsumer: SourceMapConsumer_1, BasicSourceMapConsumer: BasicSourceMapConsumer_1, IndexedSourceMapConsumer: IndexedSourceMapConsumer_1 }; var stacktraceGps = createCommonjsModule(function (module, exports) { (function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { undefined('stacktrace-gps', ['source-map', 'stackframe'], factory); } else { module.exports = factory(sourceMapConsumer, stackframe); } }(commonjsGlobal, function(SourceMap, StackFrame) { 'use strict'; /** * Make a X-Domain request to url and callback. * * @param {String} url * @returns {Promise} with response text if fulfilled */ function _xdr(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest(); req.open('get', url); req.onerror = reject; req.onreadystatechange = function onreadystatechange() { if (req.readyState === 4) { if ((req.status >= 200 && req.status < 300) || (url.substr(0, 7) === 'file://' && req.responseText)) { resolve(req.responseText); } else { reject(new Error('HTTP status: ' + req.status + ' retrieving ' + url)); } } }; req.send(); }); } /** * Convert a Base64-encoded string into its original representation. * Used for inline sourcemaps. * * @param {String} b64str Base-64 encoded string * @returns {String} original representation of the base64-encoded string. */ function _atob(b64str) { if (typeof index !== 'undefined' && index.atob) { return index.atob(b64str); } else { throw new Error('You must supply a polyfill for window.atob in this environment'); } } function _parseJson(string) { if (typeof JSON !== 'undefined' && JSON.parse) { return JSON.parse(string); } else { throw new Error('You must supply a polyfill for JSON.parse in this environment'); } } function _findFunctionName(source, lineNumber/*, columnNumber*/) { var syntaxes = [ // {name} = function ({args}) TODO args capture /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/, // function {name}({args}) m[1]=name m[2]=args /function\s+([^('"`]*?)\s*\(([^)]*)\)/, // {name} = eval() /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*(?:eval|new Function)\b/, // fn_name() { /\b(?!(?:if|for|switch|while|with|catch)\b)(?:(?:static)\s+)?(\S+)\s*\(.*?\)\s*\{/, // {name} = () => { /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*\(.*?\)\s*=>/ ]; var lines = source.split('\n'); // Walk backwards in the source lines until we find the line which matches one of the patterns above var code = ''; var maxLines = Math.min(lineNumber, 20); for (var i = 0; i < maxLines; ++i) { // lineNo is 1-based, source[] is 0-based var line = lines[lineNumber - i - 1]; var commentPos = line.indexOf('//'); if (commentPos >= 0) { line = line.substr(0, commentPos); } if (line) { code = line + code; var len = syntaxes.length; for (var index$$1 = 0; index$$1 < len; index$$1++) { var m = syntaxes[index$$1].exec(code); if (m && m[1]) { return m[1]; } } } } return undefined; } function _ensureSupportedEnvironment() { if (typeof Object.defineProperty !== 'function' || typeof Object.create !== 'function') { throw new Error('Unable to consume source maps in older browsers'); } } function _ensureStackFrameIsLegit(stackframe$$2) { if (typeof stackframe$$2 !== 'object') { throw new TypeError('Given StackFrame is not an object'); } else if (typeof stackframe$$2.fileName !== 'string') { throw new TypeError('Given file name is not a String'); } else if (typeof stackframe$$2.lineNumber !== 'number' || stackframe$$2.lineNumber % 1 !== 0 || stackframe$$2.lineNumber < 1) { throw new TypeError('Given line number must be a positive integer'); } else if (typeof stackframe$$2.columnNumber !== 'number' || stackframe$$2.columnNumber % 1 !== 0 || stackframe$$2.columnNumber < 0) { throw new TypeError('Given column number must be a non-negative integer'); } return true; } function _findSourceMappingURL(source) { var m = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/m.exec(source); if (m && m[1]) { return m[1]; } else { throw new Error('sourceMappingURL not found'); } } function _extractLocationInfoFromSourceMapSource(stackframe$$2, sourceMapConsumer$$1, sourceCache) { return new Promise(function(resolve, reject) { var loc = sourceMapConsumer$$1.originalPositionFor({ line: stackframe$$2.lineNumber, column: stackframe$$2.columnNumber }); if (loc.source) { // cache mapped sources var mappedSource = sourceMapConsumer$$1.sourceContentFor(loc.source); if (mappedSource) { sourceCache[loc.source] = mappedSource; } resolve( // given stackframe and source location, update stackframe new StackFrame({ functionName: loc.name || stackframe$$2.functionName, args: stackframe$$2.args, fileName: loc.source, lineNumber: loc.line, columnNumber: loc.column })); } else { reject(new Error('Could not get original source for given stackframe and source map')); } }); } /** * @constructor * @param {Object} opts * opts.sourceCache = {url: "Source String"} => preload source cache * opts.sourceMapConsumerCache = {/path/file.js.map: SourceMapConsumer} * opts.offline = True to prevent network requests. * Best effort without sources or source maps. * opts.ajax = Promise returning function to make X-Domain requests */ return function StackTraceGPS(opts) { if (!(this instanceof StackTraceGPS)) { return new StackTraceGPS(opts); } opts = opts || {}; this.sourceCache = opts.sourceCache || {}; this.sourceMapConsumerCache = opts.sourceMapConsumerCache || {}; this.ajax = opts.ajax || _xdr; this._atob = opts.atob || _atob; this._get = function _get(location) { return new Promise(function(resolve, reject) { var isDataUrl = location.substr(0, 5) === 'data:'; if (this.sourceCache[location]) { resolve(this.sourceCache[location]); } else if (opts.offline && !isDataUrl) { reject(new Error('Cannot make network requests in offline mode')); } else { if (isDataUrl) { // data URLs can have parameters. // see http://tools.ietf.org/html/rfc2397 var supportedEncodingRegexp = /^data:application\/json;([\w=:"-]+;)*base64,/; var match = location.match(supportedEncodingRegexp); if (match) { var sourceMapStart = match[0].length; var encodedSource = location.substr(sourceMapStart); var source = this._atob(encodedSource); this.sourceCache[location] = source; resolve(source); } else { reject(new Error('The encoding of the inline sourcemap is not supported')); } } else { var xhrPromise = this.ajax(location, {method: 'get'}); // Cache the Promise to prevent duplicate in-flight requests this.sourceCache[location] = xhrPromise; xhrPromise.then(resolve, reject); } } }.bind(this)); }; /** * Creating SourceMapConsumers is expensive, so this wraps the creation of a * SourceMapConsumer in a per-instance cache. * * @param sourceMappingURL = {String} URL to fetch source map from * @param defaultSourceRoot = Default source root for source map if undefined * @returns {Promise} that resolves a SourceMapConsumer */ this._getSourceMapConsumer = function _getSourceMapConsumer(sourceMappingURL, defaultSourceRoot) { return new Promise(function(resolve, reject) { if (this.sourceMapConsumerCache[sourceMappingURL]) { resolve(this.sourceMapConsumerCache[sourceMappingURL]); } else { var sourceMapConsumerPromise = new Promise(function(resolve, reject) { return this._get(sourceMappingURL).then(function(sourceMapSource) { if (typeof sourceMapSource === 'string') { sourceMapSource = _parseJson(sourceMapSource.replace(/^\)\]\}'/, '')); } if (typeof sourceMapSource.sourceRoot === 'undefined') { sourceMapSource.sourceRoot = defaultSourceRoot; } resolve(new SourceMap.SourceMapConsumer(sourceMapSource)); }, reject); }.bind(this)); this.sourceMapConsumerCache[sourceMappingURL] = sourceMapConsumerPromise; resolve(sourceMapConsumerPromise); } }.bind(this)); }; /** * Given a StackFrame, enhance function name and use source maps for a * better StackFrame. * * @param {StackFrame} stackframe object * @returns {Promise} that resolves with with source-mapped StackFrame */ this.pinpoint = function StackTraceGPS$$pinpoint(stackframe$$2) { return new Promise(function(resolve, reject) { this.getMappedLocation(stackframe$$2).then(function(mappedStackFrame) { function resolveMappedStackFrame() { resolve(mappedStackFrame); } this.findFunctionName(mappedStackFrame) .then(resolve, resolveMappedStackFrame) ['catch'](resolveMappedStackFrame); }.bind(this), reject); }.bind(this)); }; /** * Given a StackFrame, guess function name from location information. * * @param {StackFrame} stackframe * @returns {Promise} that resolves with enhanced StackFrame. */ this.findFunctionName = function StackTraceGPS$$findFunctionName(stackframe$$2) { return new Promise(function(resolve, reject) { _ensureStackFrameIsLegit(stackframe$$2); this._get(stackframe$$2.fileName).then(function getSourceCallback(source) { var lineNumber = stackframe$$2.lineNumber; var columnNumber = stackframe$$2.columnNumber; var guessedFunctionName = _findFunctionName(source, lineNumber, columnNumber); // Only replace functionName if we found something if (guessedFunctionName) { resolve(new StackFrame({ functionName: guessedFunctionName, args: stackframe$$2.args, fileName: stackframe$$2.fileName, lineNumber: lineNumber, columnNumber: columnNumber })); } else { resolve(stackframe$$2); } }, reject)['catch'](reject); }.bind(this)); }; /** * Given a StackFrame, seek source-mapped location and return new enhanced StackFrame. * * @param {StackFrame} stackframe * @returns {Promise} that resolves with enhanced StackFrame. */ this.getMappedLocation = function StackTraceGPS$$getMappedLocation(stackframe$$2) { return new Promise(function(resolve, reject) { _ensureSupportedEnvironment(); _ensureStackFrameIsLegit(stackframe$$2); var sourceCache = this.sourceCache; var fileName = stackframe$$2.fileName; this._get(fileName).then(function(source) { var sourceMappingURL = _findSourceMappingURL(source); var isDataUrl = sourceMappingURL.substr(0, 5) === 'data:'; var defaultSourceRoot = fileName.substring(0, fileName.lastIndexOf('/') + 1); if (sourceMappingURL[0] !== '/' && !isDataUrl && !(/^https?:\/\/|^\/\//i).test(sourceMappingURL)) { sourceMappingURL = defaultSourceRoot + sourceMappingURL; } return this._getSourceMapConsumer(sourceMappingURL, defaultSourceRoot).then(function(sourceMapConsumer$$1) { return _extractLocationInfoFromSourceMapSource(stackframe$$2, sourceMapConsumer$$1, sourceCache) .then(resolve)['catch'](function() { resolve(stackframe$$2); }); }); }.bind(this), reject)['catch'](reject); }.bind(this)); }; }; })); }); var stacktrace = createCommonjsModule(function (module, exports) { (function(root, factory) { 'use strict'; // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers. /* istanbul ignore next */ if (typeof undefined === 'function' && undefined.amd) { undefined('stacktrace', ['error-stack-parser', 'stack-generator', 'stacktrace-gps'], factory); } else { module.exports = factory(errorStackParser, stackGenerator, stacktraceGps); } }(commonjsGlobal, function StackTrace(ErrorStackParser, StackGenerator, StackTraceGPS) { var _options = { filter: function(stackframe) { // Filter out stackframes for this library by default return (stackframe.functionName || '').indexOf('StackTrace$$') === -1 && (stackframe.functionName || '').indexOf('ErrorStackParser$$') === -1 && (stackframe.functionName || '').indexOf('StackTraceGPS$$') === -1 && (stackframe.functionName || '').indexOf('StackGenerator$$') === -1; }, sourceCache: {} }; var _generateError = function StackTrace$$GenerateError() { try { // Error must be thrown to get stack in IE throw new Error(); } catch (err) { return err; } }; /** * Merge 2 given Objects. If a conflict occurs the second object wins. * Does not do deep merges. * * @param {Object} first base object * @param {Object} second overrides * @returns {Object} merged first and second * @private */ function _merge(first, second) { var target = {}; [first, second].forEach(function(obj) { for (var prop in obj) { if (obj.hasOwnProperty(prop)) { target[prop] = obj[prop]; } } return target; }); return target; } function _isShapedLikeParsableError(err) { return err.stack || err['opera#sourceloc']; } function _filtered(stackframes, filter) { if (typeof filter === 'function') { return stackframes.filter(filter); } return stackframes; } return { /** * Get a backtrace from invocation point. * * @param {Object} opts * @returns {Array} of StackFrame */ get: function StackTrace$$get(opts) { var err = _generateError(); return _isShapedLikeParsableError(err) ? this.fromError(err, opts) : this.generateArtificially(opts); }, /** * Get a backtrace from invocation point. * IMPORTANT: Does not handle source maps or guess function names! * * @param {Object} opts * @returns {Array} of StackFrame */ getSync: function StackTrace$$getSync(opts) { opts = _merge(_options, opts); var err = _generateError(); var stack = _isShapedLikeParsableError(err) ? ErrorStackParser.parse(err) : StackGenerator.backtrace(opts); return _filtered(stack, opts.filter); }, /** * Given an error object, parse it. * * @param {Error} error object * @param {Object} opts * @returns {Promise} for Array[StackFrame} */ fromError: function StackTrace$$fromError(error, opts) { opts = _merge(_options, opts); var gps = new StackTraceGPS(opts); return new Promise(function(resolve) { var stackframes = _filtered(ErrorStackParser.parse(error), opts.filter); resolve(Promise.all(stackframes.map(function(sf) { return new Promise(function(resolve) { function resolveOriginal() { resolve(sf); } gps.pinpoint(sf).then(resolve, resolveOriginal)['catch'](resolveOriginal); }); }))); }.bind(this)); }, /** * Use StackGenerator to generate a backtrace. * * @param {Object} opts * @returns {Promise} of Array[StackFrame] */ generateArtificially: function StackTrace$$generateArtificially(opts) { opts = _merge(_options, opts); var stackFrames = StackGenerator.backtrace(opts); if (typeof opts.filter === 'function') { stackFrames = stackFrames.filter(opts.filter); } return Promise.resolve(stackFrames); }, /** * Given a function, wrap it such that invocations trigger a callback that * is called with a stack trace. * * @param {Function} fn to be instrumented * @param {Function} callback function to call with a stack trace on invocation * @param {Function} errback optional function to call with error if unable to get stack trace. * @param {Object} thisArg optional context object (e.g. window) */ instrument: function StackTrace$$instrument(fn, callback, errback, thisArg) { if (typeof fn !== 'function') { throw new Error('Cannot instrument non-function object'); } else if (typeof fn.__stacktraceOriginalFn === 'function') { // Already instrumented, return given Function return fn; } var instrumented = function StackTrace$$instrumented() { try { this.get().then(callback, errback)['catch'](errback); return fn.apply(thisArg || this, arguments); } catch (e) { if (_isShapedLikeParsableError(e)) { this.fromError(e).then(callback, errback)['catch'](errback); } throw e; } }.bind(this); instrumented.__stacktraceOriginalFn = fn; return instrumented; }, /** * Given a function that has been instrumented, * revert the function to it's original (non-instrumented) state. * * @param {Function} fn to de-instrument */ deinstrument: function StackTrace$$deinstrument(fn) { if (typeof fn !== 'function') { throw new Error('Cannot de-instrument non-function object'); } else if (typeof fn.__stacktraceOriginalFn === 'function') { return fn.__stacktraceOriginalFn; } else { // Function not instrumented, return original return fn; } }, /** * Given an error message and Array of StackFrames, serialize and POST to given URL. * * @param {Array} stackframes * @param {String} url * @param {String} errorMsg * @param {Object} requestOptions */ report: function StackTrace$$report(stackframes, url, errorMsg, requestOptions) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest(); req.onerror = reject; req.onreadystatechange = function onreadystatechange() { if (req.readyState === 4) { if (req.status >= 200 && req.status < 400) { resolve(req.responseText); } else { reject(new Error('POST to ' + url + ' failed with status: ' + req.status)); } } }; req.open('post', url); // Set request headers req.setRequestHeader('Content-Type', 'application/json'); if (requestOptions && typeof requestOptions.headers === 'object') { var headers = requestOptions.headers; for (var header in headers) { if (headers.hasOwnProperty(header)) { req.setRequestHeader(header, headers[header]); } } } var reportPayload = {stack: stackframes}; if (errorMsg !== undefined && errorMsg !== null) { reportPayload.message = errorMsg; } req.send(JSON.stringify(reportPayload)); }); } }; })); }); index.stacktrace = stacktrace; function test (testDescription, ...args) { let location = stacktrace.getSync()[1]; var testFunction = args.pop(); var testCase = new TestCase(testDescription, testFunction, location, args, { parallel: true }); testHandler.register(testCase); } //# sourceMappingURL=cutest.mjs.map function createCommonjsModule$1(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var chance_1 = createCommonjsModule$1(function (module, exports) { // Chance.js 1.0.10 // http://chancejs.com // (c) 2013 Victor Quinn // Chance may be freely distributed or modified under the MIT license. (function () { // Constants var MAX_INT = 9007199254740992; var MIN_INT = -MAX_INT; var NUMBERS = '0123456789'; var CHARS_LOWER = 'abcdefghijklmnopqrstuvwxyz'; var CHARS_UPPER = CHARS_LOWER.toUpperCase(); var HEX_POOL = NUMBERS + "abcdef"; // Cached array helpers var slice = Array.prototype.slice; // Constructor function Chance (seed) { if (!(this instanceof Chance)) { if (!seed) { seed = null; } // handle other non-truthy seeds, as described in issue #322 return seed === null ? new Chance() : new Chance(seed); } // if user has provided a function, use that as the generator if (typeof seed === 'function') { this.random = seed; return this; } if (arguments.length) { // set a starting value of zero so we can add to it this.seed = 0; } // otherwise, leave this.seed blank so that MT will receive a blank for (var i = 0; i < arguments.length; i++) { var seedling = 0; if (Object.prototype.toString.call(arguments[i]) === '[object String]') { for (var j = 0; j < arguments[i].length; j++) { // create a numeric hash for each argument, add to seedling var hash = 0; for (var k = 0; k < arguments[i].length; k++) { hash = arguments[i].charCodeAt(k) + (hash << 6) + (hash << 16) - hash; } seedling += hash; } } else { seedling = arguments[i]; } this.seed += (arguments.length - i) * seedling; } // If no generator function was provided, use our MT this.mt = this.mersenne_twister(this.seed); this.bimd5 = this.blueimp_md5(); this.random = function () { return this.mt.random(this.seed); }; return this; } Chance.prototype.VERSION = "1.0.10"; // Random helper functions function initOptions(options, defaults) { options = options || {}; if (defaults) { for (var i in defaults) { if (typeof options[i] === 'undefined') { options[i] = defaults[i]; } } } return options; } function testRange(test, errorMessage) { if (test) { throw new RangeError(errorMessage); } } /** * Encode the input string with Base64. */ var base64 = function() { throw new Error('No Base64 encoder available.'); }; // Select proper Base64 encoder. (function determineBase64Encoder() { if (typeof btoa === 'function') { base64 = btoa; } else if (typeof Buffer === 'function') { base64 = function(input) { return new Buffer(input).toString('base64'); }; } })(); // -- Basics -- /** * Return a random bool, either true or false * * @param {Object} [options={ likelihood: 50 }] alter the likelihood of * receiving a true or false value back. * @throws {RangeError} if the likelihood is out of bounds * @returns {Bool} either true or false */ Chance.prototype.bool = function (options) { // likelihood of success (true) options = initOptions(options, {likelihood : 50}); // Note, we could get some minor perf optimizations by checking range // prior to initializing defaults, but that makes code a bit messier // and the check more complicated as we have to check existence of // the object then existence of the key before checking constraints. // Since the options initialization should be minor computationally, // decision made for code cleanliness intentionally. This is mentioned // here as it's the first occurrence, will not be mentioned again. testRange( options.likelihood < 0 || options.likelihood > 100, "Chance: Likelihood accepts values from 0 to 100." ); return this.random() * 100 < options.likelihood; }; /** * Return a random character. * * @param {Object} [options={}] can specify a character pool, only alpha, * only symbols, and casing (lower or upper) * @returns {String} a single random character * @throws {RangeError} Can only specify alpha or symbols, not both */ Chance.prototype.character = function (options) { options = initOptions(options); testRange( options.alpha && options.symbols, "Chance: Cannot specify both alpha and symbols." ); var symbols = "!@#$%^&*()[]", letters, pool; if (options.casing === 'lower') { letters = CHARS_LOWER; } else if (options.casing === 'upper') { letters = CHARS_UPPER; } else { letters = CHARS_LOWER + CHARS_UPPER; } if (options.pool) { pool = options.pool; } else if (options.alpha) { pool = letters; } else if (options.symbols) { pool = symbols; } else { pool = letters + NUMBERS + symbols; } return pool.charAt(this.natural({max: (pool.length - 1)})); }; // Note, wanted to use "float" or "double" but those are both JS reserved words. // Note, fixed means N OR LESS digits after the decimal. This because // It could be 14.9000 but in JavaScript, when this is cast as a number, // the trailing zeroes are dropped. Left to the consumer if trailing zeroes are // needed /** * Return a random floating point number * * @param {Object} [options={}] can specify a fixed precision, min, max * @returns {Number} a single floating point number * @throws {RangeError} Can only specify fixed or precision, not both. Also * min cannot be greater than max */ Chance.prototype.floating = function (options) { options = initOptions(options, {fixed : 4}); testRange( options.fixed && options.precision, "Chance: Cannot specify both fixed and precision." ); var num; var fixed = Math.pow(10, options.fixed); var max = MAX_INT / fixed; var min = -max; testRange( options.min && options.fixed && options.min < min, "Chance: Min specified is out of range with fixed. Min should be, at least, " + min ); testRange( options.max && options.fixed && options.max > max, "Chance: Max specified is out of range with fixed. Max should be, at most, " + max ); options = initOptions(options, { min : min, max : max }); // Todo - Make this work! // options.precision = (typeof options.precision !== "undefined") ? options.precision : false; num = this.integer({min: options.min * fixed, max: options.max * fixed}); var num_fixed = (num / fixed).toFixed(options.fixed); return parseFloat(num_fixed); }; /** * Return a random integer * * NOTE the max and min are INCLUDED in the range. So: * chance.integer({min: 1, max: 3}); * would return either 1, 2, or 3. * * @param {Object} [options={}] can specify a min and/or max * @returns {Number} a single random integer number * @throws {RangeError} min cannot be greater than max */ Chance.prototype.integer = function (options) { // 9007199254740992 (2^53) is the max integer number in JavaScript // See: http://vq.io/132sa2j options = initOptions(options, {min: MIN_INT, max: MAX_INT}); testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); return Math.floor(this.random() * (options.max - options.min + 1) + options.min); }; /** * Return a random natural * * NOTE the max and min are INCLUDED in the range. So: * chance.natural({min: 1, max: 3}); * would return either 1, 2, or 3. * * @param {Object} [options={}] can specify a min and/or maxm or a numerals count. * @returns {Number} a single random integer number * @throws {RangeError} min cannot be greater than max */ Chance.prototype.natural = function (options) { options = initOptions(options, {min: 0, max: MAX_INT}); if (typeof options.numerals === 'number'){ testRange(options.numerals < 1, "Chance: Numerals cannot be less than one."); options.min = Math.pow(10, options.numerals - 1); options.max = Math.pow(10, options.numerals) - 1; } testRange(options.min < 0, "Chance: Min cannot be less than zero."); return this.integer(options); }; /** * Return a random hex number as string * * NOTE the max and min are INCLUDED in the range. So: * chance.hex({min: '9', max: 'B'}); * would return either '9', 'A' or 'B'. * * @param {Object} [options={}] can specify a min and/or max and/or casing * @returns {String} a single random string hex number * @throws {RangeError} min cannot be greater than max */ Chance.prototype.hex = function (options) { options = initOptions(options, {min: 0, max: MAX_INT, casing: 'lower'}); testRange(options.min < 0, "Chance: Min cannot be less than zero."); var integer = this.natural({min: options.min, max: options.max}); if (options.casing === 'upper') { return integer.toString(16).toUpperCase(); } return integer.toString(16); }; /** * Return a random string * * @param {Object} [options={}] can specify a length * @returns {String} a string of random length * @throws {RangeError} length cannot be less than zero */ Chance.prototype.string = function (options) { options = initOptions(options, { length: this.natural({min: 5, max: 20}) }); testRange(options.length < 0, "Chance: Length cannot be less than zero."); var length = options.length, text = this.n(this.character, length, options); return text.join(""); }; // -- End Basics -- // -- Helpers -- Chance.prototype.capitalize = function (word) { return word.charAt(0).toUpperCase() + word.substr(1); }; Chance.prototype.mixin = function (obj) { for (var func_name in obj) { Chance.prototype[func_name] = obj[func_name]; } return this; }; /** * Given a function that generates something random and a number of items to generate, * return an array of items where none repeat. * * @param {Function} fn the function that generates something random * @param {Number} num number of terms to generate * @param {Object} options any options to pass on to the generator function * @returns {Array} an array of length `num` with every item generated by `fn` and unique * * There can be more parameters after these. All additional parameters are provided to the given function */ Chance.prototype.unique = function(fn, num, options) { testRange( typeof fn !== "function", "Chance: The first argument must be a function." ); var comparator = function(arr, val) { return arr.indexOf(val) !== -1; }; if (options) { comparator = options.comparator || comparator; } var arr = [], count = 0, result, MAX_DUPLICATES = num * 50, params = slice.call(arguments, 2); while (arr.length < num) { var clonedParams = JSON.parse(JSON.stringify(params)); result = fn.apply(this, clonedParams); if (!comparator(arr, result)) { arr.push(result); // reset count when unique found count = 0; } if (++count > MAX_DUPLICATES) { throw new RangeError("Chance: num is likely too large for sample set"); } } return arr; }; /** * Gives an array of n random terms * * @param {Function} fn the function that generates something random * @param {Number} n number of terms to generate * @returns {Array} an array of length `n` with items generated by `fn` * * There can be more parameters after these. All additional parameters are provided to the given function */ Chance.prototype.n = function(fn, n) { testRange( typeof fn !== "function", "Chance: The first argument must be a function." ); if (typeof n === 'undefined') { n = 1; } var i = n, arr = [], params = slice.call(arguments, 2); // Providing a negative count should result in a noop. i = Math.max( 0, i ); for (null; i--; null) { arr.push(fn.apply(this, params)); } return arr; }; // H/T to SO for this one: http://vq.io/OtUrZ5 Chance.prototype.pad = function (number, width, pad) { // Default pad to 0 if none provided pad = pad || '0'; // Convert number to a string number = number + ''; return number.length >= width ? number : new Array(width - number.length + 1).join(pad) + number; }; // DEPRECATED on 2015-10-01 Chance.prototype.pick = function (arr, count) { if (arr.length === 0) { throw new RangeError("Chance: Cannot pick() from an empty array"); } if (!count || count === 1) { return arr[this.natural({max: arr.length - 1})]; } else { return this.shuffle(arr).slice(0, count); } }; // Given an array, returns a single random element Chance.prototype.pickone = function (arr) { if (arr.length === 0) { throw new RangeError("Chance: Cannot pickone() from an empty array"); } return arr[this.natural({max: arr.length - 1})]; }; // Given an array, returns a random set with 'count' elements Chance.prototype.pickset = function (arr, count) { if (count === 0) { return []; } if (arr.length === 0) { throw new RangeError("Chance: Cannot pickset() from an empty array"); } if (count < 0) { throw new RangeError("Chance: Count must be a positive number"); } if (!count || count === 1) { return [ this.pickone(arr) ]; } else { return this.shuffle(arr).slice(0, count); } }; Chance.prototype.shuffle = function (arr) { var old_array = arr.slice(0), new_array = [], j = 0, length = Number(old_array.length); for (var i = 0; i < length; i++) { // Pick a random index from the array j = this.natural({max: old_array.length - 1}); // Add it to the new array new_array[i] = old_array[j]; // Remove that element from the original array old_array.splice(j, 1); } return new_array; }; // Returns a single item from an array with relative weighting of odds Chance.prototype.weighted = function (arr, weights, trim) { if (arr.length !== weights.length) { throw new RangeError("Chance: Length of array and weights must match"); } // scan weights array and sum valid entries var sum = 0; var val; for (var weightIndex = 0; weightIndex < weights.length; ++weightIndex) { val = weights[weightIndex]; if (isNaN(val)) { throw new RangeError("Chance: All weights must be numbers"); } if (val > 0) { sum += val; } } if (sum === 0) { throw new RangeError("Chance: No valid entries in array weights"); } // select a value within range var selected = this.random() * sum; // find array entry corresponding to selected value var total = 0; var lastGoodIdx = -1; var chosenIdx; for (weightIndex = 0; weightIndex < weights.length; ++weightIndex) { val = weights[weightIndex]; total += val; if (val > 0) { if (selected <= total) { chosenIdx = weightIndex; break; } lastGoodIdx = weightIndex; } // handle any possible rounding error comparison to ensure something is picked if (weightIndex === (weights.length - 1)) { chosenIdx = lastGoodIdx; } } var chosen = arr[chosenIdx]; trim = (typeof trim === 'undefined') ? false : trim; if (trim) { arr.splice(chosenIdx, 1); weights.splice(chosenIdx, 1); } return chosen; }; // -- End Helpers -- // -- Text -- Chance.prototype.paragraph = function (options) { options = initOptions(options); var sentences = options.sentences || this.natural({min: 3, max: 7}), sentence_array = this.n(this.sentence, sentences); return sentence_array.join(' '); }; // Could get smarter about this than generating random words and // chaining them together. Such as: http://vq.io/1a5ceOh Chance.prototype.sentence = function (options) { options = initOptions(options); var words = options.words || this.natural({min: 12, max: 18}), punctuation = options.punctuation, text, word_array = this.n(this.word, words); text = word_array.join(' '); // Capitalize first letter of sentence text = this.capitalize(text); // Make sure punctuation has a usable value if (punctuation !== false && !/^[\.\?;!:]$/.test(punctuation)) { punctuation = '.'; } // Add punctuation mark if (punctuation) { text += punctuation; } return text; }; Chance.prototype.syllable = function (options) { options = initOptions(options); var length = options.length || this.natural({min: 2, max: 3}), consonants = 'bcdfghjklmnprstvwz', // consonants except hard to speak ones vowels = 'aeiou', // vowels all = consonants + vowels, // all text = '', chr; // I'm sure there's a more elegant way to do this, but this works // decently well. for (var i = 0; i < length; i++) { if (i === 0) { // First character can be anything chr = this.character({pool: all}); } else if (consonants.indexOf(chr) === -1) { // Last character was a vowel, now we want a consonant chr = this.character({pool: consonants}); } else { // Last character was a consonant, now we want a vowel chr = this.character({pool: vowels}); } text += chr; } if (options.capitalize) { text = this.capitalize(text); } return text; }; Chance.prototype.word = function (options) { options = initOptions(options); testRange( options.syllables && options.length, "Chance: Cannot specify both syllables AND length." ); var syllables = options.syllables || this.natural({min: 1, max: 3}), text = ''; if (options.length) { // Either bound word by length do { text += this.syllable(); } while (text.length < options.length); text = text.substring(0, options.length); } else { // Or by number of syllables for (var i = 0; i < syllables; i++) { text += this.syllable(); } } if (options.capitalize) { text = this.capitalize(text); } return text; }; // -- End Text -- // -- Person -- Chance.prototype.age = function (options) { options = initOptions(options); var ageRange; switch (options.type) { case 'child': ageRange = {min: 0, max: 12}; break; case 'teen': ageRange = {min: 13, max: 19}; break; case 'adult': ageRange = {min: 18, max: 65}; break; case 'senior': ageRange = {min: 65, max: 100}; break; case 'all': ageRange = {min: 0, max: 100}; break; default: ageRange = {min: 18, max: 65}; break; } return this.natural(ageRange); }; Chance.prototype.birthday = function (options) { var age = this.age(options); var currentYear = new Date().getFullYear(); if (options && options.type) { var min = new Date(); var max = new Date(); min.setFullYear(currentYear - age - 1); max.setFullYear(currentYear - age); options = initOptions(options, { min: min, max: max }); } else { options = initOptions(options, { year: currentYear - age }); } return this.date(options); }; // CPF; ID to identify taxpayers in Brazil Chance.prototype.cpf = function (options) { options = initOptions(options, { formatted: true }); var n = this.n(this.natural, 9, { max: 9 }); var d1 = n[8]*2+n[7]*3+n[6]*4+n[5]*5+n[4]*6+n[3]*7+n[2]*8+n[1]*9+n[0]*10; d1 = 11 - (d1 % 11); if (d1>=10) { d1 = 0; } var d2 = d1*2+n[8]*3+n[7]*4+n[6]*5+n[5]*6+n[4]*7+n[3]*8+n[2]*9+n[1]*10+n[0]*11; d2 = 11 - (d2 % 11); if (d2>=10) { d2 = 0; } var cpf = ''+n[0]+n[1]+n[2]+'.'+n[3]+n[4]+n[5]+'.'+n[6]+n[7]+n[8]+'-'+d1+d2; return options.formatted ? cpf : cpf.replace(/\D/g,''); }; // CNPJ: ID to identify companies in Brazil Chance.prototype.cnpj = function (options) { options = initOptions(options, { formatted: true }); var n = this.n(this.natural, 12, { max: 12 }); var d1 = n[11]*2+n[10]*3+n[9]*4+n[8]*5+n[7]*6+n[6]*7+n[5]*8+n[4]*9+n[3]*2+n[2]*3+n[1]*4+n[0]*5; d1 = 11 - (d1 % 11); if (d1<2) { d1 = 0; } var d2 = d1*2+n[11]*3+n[10]*4+n[9]*5+n[8]*6+n[7]*7+n[6]*8+n[5]*9+n[4]*2+n[3]*3+n[2]*4+n[1]*5+n[0]*6; d2 = 11 - (d2 % 11); if (d2<2) { d2 = 0; } var cnpj = ''+n[0]+n[1]+'.'+n[2]+n[3]+n[4]+'.'+n[5]+n[6]+n[7]+'/'+n[8]+n[9]+n[10]+n[11]+'-'+d1+d2; return options.formatted ? cnpj : cnpj.replace(/\D/g,''); }; Chance.prototype.first = function (options) { options = initOptions(options, {gender: this.gender(), nationality: 'en'}); return this.pick(this.get("firstNames")[options.gender.toLowerCase()][options.nationality.toLowerCase()]); }; Chance.prototype.profession = function (options) { options = initOptions(options); if(options.rank){ return this.pick(['Apprentice ', 'Junior ', 'Senior ', 'Lead ']) + this.pick(this.get("profession")); } else{ return this.pick(this.get("profession")); } }; Chance.prototype.company = function (){ return this.pick(this.get("company")); }; Chance.prototype.gender = function (options) { options = initOptions(options, {extraGenders: []}); return this.pick(['Male', 'Female'].concat(options.extraGenders)); }; Chance.prototype.last = function (options) { options = initOptions(options, {nationality: 'en'}); return this.pick(this.get("lastNames")[options.nationality.toLowerCase()]); }; Chance.prototype.israelId=function(){ var x=this.string({pool: '0123456789',length:8}); var y=0; for (var i=0;i hex * -> rgb * -> rgba * -> 0x * -> named color * * #Examples: * =============================================== * * Geerate random hex color * chance.color() => '#79c157' / 'rgb(110,52,164)' / '0x67ae0b' / '#e2e2e2' / '#29CFA7' * * * Generate Hex based color value * chance.color({format: 'hex'}) => '#d67118' * * * Generate simple rgb value * chance.color({format: 'rgb'}) => 'rgb(110,52,164)' * * * Generate Ox based color value * chance.color({format: '0x'}) => '0x67ae0b' * * * Generate graiscale based value * chance.color({grayscale: true}) => '#e2e2e2' * * * Return valide color name * chance.color({format: 'name'}) => 'red' * * * Make color uppercase * chance.color({casing: 'upper'}) => '#29CFA7' * * * Min Max values for RGBA * var light_red = chance.color({format: 'hex', min_red: 200, max_red: 255, max_green: 0, max_blue: 0, min_alpha: .2, max_alpha: .3}); * * @param [object] options * @return [string] color value */ Chance.prototype.color = function (options) { function gray(value, delimiter) { return [value, value, value].join(delimiter || ''); } function rgb(hasAlpha) { var rgbValue = (hasAlpha) ? 'rgba' : 'rgb'; var alphaChannel = (hasAlpha) ? (',' + this.floating({min:min_alpha, max:max_alpha})) : ""; var colorValue = (isGrayscale) ? (gray(this.natural({min: min_rgb, max: max_rgb}), ',')) : (this.natural({min: min_green, max: max_green}) + ',' + this.natural({min: min_blue, max: max_blue}) + ',' + this.natural({max: 255})); return rgbValue + '(' + colorValue + alphaChannel + ')'; } function hex(start, end, withHash) { var symbol = (withHash) ? "#" : ""; var hexstring = ""; if (isGrayscale) { hexstring = gray(this.pad(this.hex({min: min_rgb, max: max_rgb}), 2)); if (options.format === "shorthex") { hexstring = gray(this.hex({min: 0, max: 15})); } } else { if (options.format === "shorthex") { hexstring = this.pad(this.hex({min: Math.floor(min_red / 16), max: Math.floor(max_red / 16)}), 1) + this.pad(this.hex({min: Math.floor(min_green / 16), max: Math.floor(max_green / 16)}), 1) + this.pad(this.hex({min: Math.floor(min_blue / 16), max: Math.floor(max_blue / 16)}), 1); } else if (min_red !== undefined || max_red !== undefined || min_green !== undefined || max_green !== undefined || min_blue !== undefined || max_blue !== undefined) { hexstring = this.pad(this.hex({min: min_red, max: max_red}), 2) + this.pad(this.hex({min: min_green, max: max_green}), 2) + this.pad(this.hex({min: min_blue, max: max_blue}), 2); } else { hexstring = this.pad(this.hex({min: min_rgb, max: max_rgb}), 2) + this.pad(this.hex({min: min_rgb, max: max_rgb}), 2) + this.pad(this.hex({min: min_rgb, max: max_rgb}), 2); } } return symbol + hexstring; } options = initOptions(options, { format: this.pick(['hex', 'shorthex', 'rgb', 'rgba', '0x', 'name']), grayscale: false, casing: 'lower', min: 0, max: 255, min_red: undefined, max_red: undefined, min_green: undefined, max_green: undefined, min_blue: undefined, max_blue: undefined, min_alpha: 0, max_alpha: 1 }); var isGrayscale = options.grayscale; var min_rgb = options.min; var max_rgb = options.max; var min_red = options.min_red; var max_red = options.max_red; var min_green = options.min_green; var max_green = options.max_green; var min_blue = options.min_blue; var max_blue = options.max_blue; var min_alpha = options.min_alpha; var max_alpha = options.max_alpha; if (options.min_red === undefined) { min_red = min_rgb; } if (options.max_red === undefined) { max_red = max_rgb; } if (options.min_green === undefined) { min_green = min_rgb; } if (options.max_green === undefined) { max_green = max_rgb; } if (options.min_blue === undefined) { min_blue = min_rgb; } if (options.max_blue === undefined) { max_blue = max_rgb; } if (options.min_alpha === undefined) { min_alpha = 0; } if (options.max_alpha === undefined) { max_alpha = 1; } if (isGrayscale && min_rgb === 0 && max_rgb === 255 && min_red !== undefined && max_red !== undefined) { min_rgb = ((min_red + min_green + min_blue) / 3); max_rgb = ((max_red + max_green + max_blue) / 3); } var colorValue; if (options.format === 'hex') { colorValue = hex.call(this, 2, 6, true); } else if (options.format === 'shorthex') { colorValue = hex.call(this, 1, 3, true); } else if (options.format === 'rgb') { colorValue = rgb.call(this, false); } else if (options.format === 'rgba') { colorValue = rgb.call(this, true); } else if (options.format === '0x') { colorValue = '0x' + hex.call(this, 2, 6); } else if(options.format === 'name') { return this.pick(this.get("colorNames")); } else { throw new RangeError('Invalid format provided. Please provide one of "hex", "shorthex", "rgb", "rgba", "0x" or "name".'); } if (options.casing === 'upper' ) { colorValue = colorValue.toUpperCase(); } return colorValue; }; Chance.prototype.domain = function (options) { options = initOptions(options); return this.word() + '.' + (options.tld || this.tld()); }; Chance.prototype.email = function (options) { options = initOptions(options); return this.word({length: options.length}) + '@' + (options.domain || this.domain()); }; Chance.prototype.fbid = function () { return parseInt('10000' + this.natural({max: 100000000000}), 10); }; Chance.prototype.google_analytics = function () { var account = this.pad(this.natural({max: 999999}), 6); var property = this.pad(this.natural({max: 99}), 2); return 'UA-' + account + '-' + property; }; Chance.prototype.hashtag = function () { return '#' + this.word(); }; Chance.prototype.ip = function () { // Todo: This could return some reserved IPs. See http://vq.io/137dgYy // this should probably be updated to account for that rare as it may be return this.natural({min: 1, max: 254}) + '.' + this.natural({max: 255}) + '.' + this.natural({max: 255}) + '.' + this.natural({min: 1, max: 254}); }; Chance.prototype.ipv6 = function () { var ip_addr = this.n(this.hash, 8, {length: 4}); return ip_addr.join(":"); }; Chance.prototype.klout = function () { return this.natural({min: 1, max: 99}); }; Chance.prototype.semver = function (options) { options = initOptions(options, { include_prerelease: true }); var range = this.pickone(["^", "~", "<", ">", "<=", ">=", "="]); if (options.range) { range = options.range; } var prerelease = ""; if (options.include_prerelease) { prerelease = this.weighted(["", "-dev", "-beta", "-alpha"], [50, 10, 5, 1]); } return range + this.rpg('3d10').join('.') + prerelease; }; Chance.prototype.tlds = function () { return ['com', 'org', 'edu', 'gov', 'co.uk', 'net', 'io', 'ac', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'as', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi', 'bj', 'bm', 'bn', 'bo', 'bq', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cr', 'cu', 'cv', 'cw', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm', 'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'ss', 'st', 'su', 'sv', 'sx', 'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua', 'ug', 'uk', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'za', 'zm', 'zw']; }; Chance.prototype.tld = function () { return this.pick(this.tlds()); }; Chance.prototype.twitter = function () { return '@' + this.word(); }; Chance.prototype.url = function (options) { options = initOptions(options, { protocol: "http", domain: this.domain(options), domain_prefix: "", path: this.word(), extensions: []}); var extension = options.extensions.length > 0 ? "." + this.pick(options.extensions) : ""; var domain = options.domain_prefix ? options.domain_prefix + "." + options.domain : options.domain; return options.protocol + "://" + domain + "/" + options.path + extension; }; Chance.prototype.port = function() { return this.integer({min: 0, max: 65535}); }; Chance.prototype.locale = function (options) { options = initOptions(options); if (options.region){ return this.pick(this.get("locale_regions")); } else { return this.pick(this.get("locale_languages")); } }; Chance.prototype.locales = function (options) { options = initOptions(options); if (options.region){ return this.get("locale_regions"); } else { return this.get("locale_languages"); } }; // -- End Web -- // -- Location -- Chance.prototype.address = function (options) { options = initOptions(options); return this.natural({min: 5, max: 2000}) + ' ' + this.street(options); }; Chance.prototype.altitude = function (options) { options = initOptions(options, {fixed: 5, min: 0, max: 8848}); return this.floating({ min: options.min, max: options.max, fixed: options.fixed }); }; Chance.prototype.areacode = function (options) { options = initOptions(options, {parens : true}); // Don't want area codes to start with 1, or have a 9 as the second digit var areacode = this.natural({min: 2, max: 9}).toString() + this.natural({min: 0, max: 8}).toString() + this.natural({min: 0, max: 9}).toString(); return options.parens ? '(' + areacode + ')' : areacode; }; Chance.prototype.city = function () { return this.capitalize(this.word({syllables: 3})); }; Chance.prototype.coordinates = function (options) { return this.latitude(options) + ', ' + this.longitude(options); }; Chance.prototype.countries = function () { return this.get("countries"); }; Chance.prototype.country = function (options) { options = initOptions(options); var country = this.pick(this.countries()); return options.full ? country.name : country.abbreviation; }; Chance.prototype.depth = function (options) { options = initOptions(options, {fixed: 5, min: -10994, max: 0}); return this.floating({ min: options.min, max: options.max, fixed: options.fixed }); }; Chance.prototype.geohash = function (options) { options = initOptions(options, { length: 7 }); return this.string({ length: options.length, pool: '0123456789bcdefghjkmnpqrstuvwxyz' }); }; Chance.prototype.geojson = function (options) { return this.latitude(options) + ', ' + this.longitude(options) + ', ' + this.altitude(options); }; Chance.prototype.latitude = function (options) { options = initOptions(options, {fixed: 5, min: -90, max: 90}); return this.floating({min: options.min, max: options.max, fixed: options.fixed}); }; Chance.prototype.longitude = function (options) { options = initOptions(options, {fixed: 5, min: -180, max: 180}); return this.floating({min: options.min, max: options.max, fixed: options.fixed}); }; Chance.prototype.phone = function (options) { var self = this, numPick, ukNum = function (parts) { var section = []; //fills the section part of the phone number with random numbers. parts.sections.forEach(function(n) { section.push(self.string({ pool: '0123456789', length: n})); }); return parts.area + section.join(' '); }; options = initOptions(options, { formatted: true, country: 'us', mobile: false }); if (!options.formatted) { options.parens = false; } var phone; switch (options.country) { case 'fr': if (!options.mobile) { numPick = this.pick([ // Valid zone and département codes. '01' + this.pick(['30', '34', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '53', '55', '56', '58', '60', '64', '69', '70', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83']) + self.string({ pool: '0123456789', length: 6}), '02' + this.pick(['14', '18', '22', '23', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '40', '41', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '56', '57', '61', '62', '69', '72', '76', '77', '78', '85', '90', '96', '97', '98', '99']) + self.string({ pool: '0123456789', length: 6}), '03' + this.pick(['10', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '39', '44', '45', '51', '52', '54', '55', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90']) + self.string({ pool: '0123456789', length: 6}), '04' + this.pick(['11', '13', '15', '20', '22', '26', '27', '30', '32', '34', '37', '42', '43', '44', '50', '56', '57', '63', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '88', '89', '90', '91', '92', '93', '94', '95', '97', '98']) + self.string({ pool: '0123456789', length: 6}), '05' + this.pick(['08', '16', '17', '19', '24', '31', '32', '33', '34', '35', '40', '45', '46', '47', '49', '53', '55', '56', '57', '58', '59', '61', '62', '63', '64', '65', '67', '79', '81', '82', '86', '87', '90', '94']) + self.string({ pool: '0123456789', length: 6}), '09' + self.string({ pool: '0123456789', length: 8}), ]); phone = options.formatted ? numPick.match(/../g).join(' ') : numPick; } else { numPick = this.pick(['06', '07']) + self.string({ pool: '0123456789', length: 8}); phone = options.formatted ? numPick.match(/../g).join(' ') : numPick; } break; case 'uk': if (!options.mobile) { numPick = this.pick([ //valid area codes of major cities/counties followed by random numbers in required format. { area: '01' + this.character({ pool: '234569' }) + '1 ', sections: [3,4] }, { area: '020 ' + this.character({ pool: '378' }), sections: [3,4] }, { area: '023 ' + this.character({ pool: '89' }), sections: [3,4] }, { area: '024 7', sections: [3,4] }, { area: '028 ' + this.pick(['25','28','37','71','82','90','92','95']), sections: [2,4] }, { area: '012' + this.pick(['04','08','54','76','97','98']) + ' ', sections: [6] }, { area: '013' + this.pick(['63','64','84','86']) + ' ', sections: [6] }, { area: '014' + this.pick(['04','20','60','61','80','88']) + ' ', sections: [6] }, { area: '015' + this.pick(['24','27','62','66']) + ' ', sections: [6] }, { area: '016' + this.pick(['06','29','35','47','59','95']) + ' ', sections: [6] }, { area: '017' + this.pick(['26','44','50','68']) + ' ', sections: [6] }, { area: '018' + this.pick(['27','37','84','97']) + ' ', sections: [6] }, { area: '019' + this.pick(['00','05','35','46','49','63','95']) + ' ', sections: [6] } ]); phone = options.formatted ? ukNum(numPick) : ukNum(numPick).replace(' ', '', 'g'); } else { numPick = this.pick([ { area: '07' + this.pick(['4','5','7','8','9']), sections: [2,6] }, { area: '07624 ', sections: [6] } ]); phone = options.formatted ? ukNum(numPick) : ukNum(numPick).replace(' ', ''); } break; case 'za': if (!options.mobile) { numPick = this.pick([ '01' + this.pick(['0', '1', '2', '3', '4', '5', '6', '7', '8']) + self.string({ pool: '0123456789', length: 7}), '02' + this.pick(['1', '2', '3', '4', '7', '8']) + self.string({ pool: '0123456789', length: 7}), '03' + this.pick(['1', '2', '3', '5', '6', '9']) + self.string({ pool: '0123456789', length: 7}), '04' + this.pick(['1', '2', '3', '4', '5','6','7', '8','9']) + self.string({ pool: '0123456789', length: 7}), '05' + this.pick(['1', '3', '4', '6', '7', '8']) + self.string({ pool: '0123456789', length: 7}), ]); phone = options.formatted || numPick; } else { numPick = this.pick([ '060' + this.pick(['3','4','5','6','7','8','9']) + self.string({ pool: '0123456789', length: 6}), '061' + this.pick(['0','1','2','3','4','5','8']) + self.string({ pool: '0123456789', length: 6}), '06' + self.string({ pool: '0123456789', length: 7}), '071' + this.pick(['0','1','2','3','4','5','6','7','8','9']) + self.string({ pool: '0123456789', length: 6}), '07' + this.pick(['2','3','4','6','7','8','9']) + self.string({ pool: '0123456789', length: 7}), '08' + this.pick(['0','1','2','3','4','5']) + self.string({ pool: '0123456789', length: 7}), ]); phone = options.formatted || numPick; } break; case 'us': var areacode = this.areacode(options).toString(); var exchange = this.natural({ min: 2, max: 9 }).toString() + this.natural({ min: 0, max: 9 }).toString() + this.natural({ min: 0, max: 9 }).toString(); var subscriber = this.natural({ min: 1000, max: 9999 }).toString(); // this could be random [0-9]{4} phone = options.formatted ? areacode + ' ' + exchange + '-' + subscriber : areacode + exchange + subscriber; } return phone; }; Chance.prototype.postal = function () { // Postal District var pd = this.character({pool: "XVTSRPNKLMHJGECBA"}); // Forward Sortation Area (FSA) var fsa = pd + this.natural({max: 9}) + this.character({alpha: true, casing: "upper"}); // Local Delivery Unut (LDU) var ldu = this.natural({max: 9}) + this.character({alpha: true, casing: "upper"}) + this.natural({max: 9}); return fsa + " " + ldu; }; Chance.prototype.counties = function (options) { options = initOptions(options, { country: 'uk' }); return this.get("counties")[options.country.toLowerCase()]; }; Chance.prototype.county = function (options) { return this.pick(this.counties(options)).name; }; Chance.prototype.provinces = function (options) { options = initOptions(options, { country: 'ca' }); return this.get("provinces")[options.country.toLowerCase()]; }; Chance.prototype.province = function (options) { return (options && options.full) ? this.pick(this.provinces(options)).name : this.pick(this.provinces(options)).abbreviation; }; Chance.prototype.state = function (options) { return (options && options.full) ? this.pick(this.states(options)).name : this.pick(this.states(options)).abbreviation; }; Chance.prototype.states = function (options) { options = initOptions(options, { country: 'us', us_states_and_dc: true } ); var states; switch (options.country.toLowerCase()) { case 'us': var us_states_and_dc = this.get("us_states_and_dc"), territories = this.get("territories"), armed_forces = this.get("armed_forces"); states = []; if (options.us_states_and_dc) { states = states.concat(us_states_and_dc); } if (options.territories) { states = states.concat(territories); } if (options.armed_forces) { states = states.concat(armed_forces); } break; case 'it': states = this.get("country_regions")[options.country.toLowerCase()]; break; case 'uk': states = this.get("counties")[options.country.toLowerCase()]; break; } return states; }; Chance.prototype.street = function (options) { options = initOptions(options, { country: 'us', syllables: 2 }); var street; switch (options.country.toLowerCase()) { case 'us': street = this.word({ syllables: options.syllables }); street = this.capitalize(street); street += ' '; street += options.short_suffix ? this.street_suffix(options).abbreviation : this.street_suffix(options).name; break; case 'it': street = this.word({ syllables: options.syllables }); street = this.capitalize(street); street = (options.short_suffix ? this.street_suffix(options).abbreviation : this.street_suffix(options).name) + " " + street; break; } return street; }; Chance.prototype.street_suffix = function (options) { options = initOptions(options, { country: 'us' }); return this.pick(this.street_suffixes(options)); }; Chance.prototype.street_suffixes = function (options) { options = initOptions(options, { country: 'us' }); // These are the most common suffixes. return this.get("street_suffixes")[options.country.toLowerCase()]; }; // Note: only returning US zip codes, internationalization will be a whole // other beast to tackle at some point. Chance.prototype.zip = function (options) { var zip = this.n(this.natural, 5, {max: 9}); if (options && options.plusfour === true) { zip.push('-'); zip = zip.concat(this.n(this.natural, 4, {max: 9})); } return zip.join(""); }; // -- End Location -- // -- Time Chance.prototype.ampm = function () { return this.bool() ? 'am' : 'pm'; }; Chance.prototype.date = function (options) { var date_string, date; // If interval is specified we ignore preset if(options && (options.min || options.max)) { options = initOptions(options, { american: true, string: false }); var min = typeof options.min !== "undefined" ? options.min.getTime() : 1; // 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. http://es5.github.io/#x15.9.1.1 var max = typeof options.max !== "undefined" ? options.max.getTime() : 8640000000000000; date = new Date(this.integer({min: min, max: max})); } else { var m = this.month({raw: true}); var daysInMonth = m.days; if(options && options.month) { // Mod 12 to allow months outside range of 0-11 (not encouraged, but also not prevented). daysInMonth = this.get('months')[((options.month % 12) + 12) % 12].days; } options = initOptions(options, { year: parseInt(this.year(), 10), // Necessary to subtract 1 because Date() 0-indexes month but not day or year // for some reason. month: m.numeric - 1, day: this.natural({min: 1, max: daysInMonth}), hour: this.hour({twentyfour: true}), minute: this.minute(), second: this.second(), millisecond: this.millisecond(), american: true, string: false }); date = new Date(options.year, options.month, options.day, options.hour, options.minute, options.second, options.millisecond); } if (options.american) { // Adding 1 to the month is necessary because Date() 0-indexes // months but not day for some odd reason. date_string = (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear(); } else { date_string = date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear(); } return options.string ? date_string : date; }; Chance.prototype.hammertime = function (options) { return this.date(options).getTime(); }; Chance.prototype.hour = function (options) { options = initOptions(options, { min: options && options.twentyfour ? 0 : 1, max: options && options.twentyfour ? 23 : 12 }); testRange(options.min < 0, "Chance: Min cannot be less than 0."); testRange(options.twentyfour && options.max > 23, "Chance: Max cannot be greater than 23 for twentyfour option."); testRange(!options.twentyfour && options.max > 12, "Chance: Max cannot be greater than 12."); testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); return this.natural({min: options.min, max: options.max}); }; Chance.prototype.millisecond = function () { return this.natural({max: 999}); }; Chance.prototype.minute = Chance.prototype.second = function (options) { options = initOptions(options, {min: 0, max: 59}); testRange(options.min < 0, "Chance: Min cannot be less than 0."); testRange(options.max > 59, "Chance: Max cannot be greater than 59."); testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); return this.natural({min: options.min, max: options.max}); }; Chance.prototype.month = function (options) { options = initOptions(options, {min: 1, max: 12}); testRange(options.min < 1, "Chance: Min cannot be less than 1."); testRange(options.max > 12, "Chance: Max cannot be greater than 12."); testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); var month = this.pick(this.months().slice(options.min - 1, options.max)); return options.raw ? month : month.name; }; Chance.prototype.months = function () { return this.get("months"); }; Chance.prototype.second = function () { return this.natural({max: 59}); }; Chance.prototype.timestamp = function () { return this.natural({min: 1, max: parseInt(new Date().getTime() / 1000, 10)}); }; Chance.prototype.weekday = function (options) { options = initOptions(options, {weekday_only: false}); var weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]; if (!options.weekday_only) { weekdays.push("Saturday"); weekdays.push("Sunday"); } return this.pickone(weekdays); }; Chance.prototype.year = function (options) { // Default to current year as min if none specified options = initOptions(options, {min: new Date().getFullYear()}); // Default to one century after current year as max if none specified options.max = (typeof options.max !== "undefined") ? options.max : options.min + 100; return this.natural(options).toString(); }; // -- End Time // -- Finance -- Chance.prototype.cc = function (options) { options = initOptions(options); var type, number, to_generate; type = (options.type) ? this.cc_type({ name: options.type, raw: true }) : this.cc_type({ raw: true }); number = type.prefix.split(""); to_generate = type.length - type.prefix.length - 1; // Generates n - 1 digits number = number.concat(this.n(this.integer, to_generate, {min: 0, max: 9})); // Generates the last digit according to Luhn algorithm number.push(this.luhn_calculate(number.join(""))); return number.join(""); }; Chance.prototype.cc_types = function () { // http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29 return this.get("cc_types"); }; Chance.prototype.cc_type = function (options) { options = initOptions(options); var types = this.cc_types(), type = null; if (options.name) { for (var i = 0; i < types.length; i++) { // Accept either name or short_name to specify card type if (types[i].name === options.name || types[i].short_name === options.name) { type = types[i]; break; } } if (type === null) { throw new RangeError("Chance: Credit card type '" + options.name + "' is not supported"); } } else { type = this.pick(types); } return options.raw ? type : type.name; }; // return all world currency by ISO 4217 Chance.prototype.currency_types = function () { return this.get("currency_types"); }; // return random world currency by ISO 4217 Chance.prototype.currency = function () { return this.pick(this.currency_types()); }; // return all timezones available Chance.prototype.timezones = function () { return this.get("timezones"); }; // return random timezone Chance.prototype.timezone = function () { return this.pick(this.timezones()); }; //Return random correct currency exchange pair (e.g. EUR/USD) or array of currency code Chance.prototype.currency_pair = function (returnAsString) { var currencies = this.unique(this.currency, 2, { comparator: function(arr, val) { return arr.reduce(function(acc, item) { // If a match has been found, short circuit check and just return return acc || (item.code === val.code); }, false); } }); if (returnAsString) { return currencies[0].code + '/' + currencies[1].code; } else { return currencies; } }; Chance.prototype.dollar = function (options) { // By default, a somewhat more sane max for dollar than all available numbers options = initOptions(options, {max : 10000, min : 0}); var dollar = this.floating({min: options.min, max: options.max, fixed: 2}).toString(), cents = dollar.split('.')[1]; if (cents === undefined) { dollar += '.00'; } else if (cents.length < 2) { dollar = dollar + '0'; } if (dollar < 0) { return '-$' + dollar.replace('-', ''); } else { return '$' + dollar; } }; Chance.prototype.euro = function (options) { return Number(this.dollar(options).replace("$", "")).toLocaleString() + "€"; }; Chance.prototype.exp = function (options) { options = initOptions(options); var exp = {}; exp.year = this.exp_year(); // If the year is this year, need to ensure month is greater than the // current month or this expiration will not be valid if (exp.year === (new Date().getFullYear()).toString()) { exp.month = this.exp_month({future: true}); } else { exp.month = this.exp_month(); } return options.raw ? exp : exp.month + '/' + exp.year; }; Chance.prototype.exp_month = function (options) { options = initOptions(options); var month, month_int, // Date object months are 0 indexed curMonth = new Date().getMonth() + 1; if (options.future && (curMonth !== 12)) { do { month = this.month({raw: true}).numeric; month_int = parseInt(month, 10); } while (month_int <= curMonth); } else { month = this.month({raw: true}).numeric; } return month; }; Chance.prototype.exp_year = function () { var curMonth = new Date().getMonth() + 1, curYear = new Date().getFullYear(); return this.year({min: ((curMonth === 12) ? (curYear + 1) : curYear), max: (curYear + 10)}); }; Chance.prototype.vat = function (options) { options = initOptions(options, { country: 'it' }); switch (options.country.toLowerCase()) { case 'it': return this.it_vat(); } }; /** * Generate a string matching IBAN pattern (https://en.wikipedia.org/wiki/International_Bank_Account_Number). * No country-specific formats support (yet) */ Chance.prototype.iban = function () { var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var alphanum = alpha + '0123456789'; var iban = this.string({ length: 2, pool: alpha }) + this.pad(this.integer({ min: 0, max: 99 }), 2) + this.string({ length: 4, pool: alphanum }) + this.pad(this.natural(), this.natural({ min: 6, max: 26 })); return iban; }; // -- End Finance // -- Regional Chance.prototype.it_vat = function () { var it_vat = this.natural({min: 1, max: 1800000}); it_vat = this.pad(it_vat, 7) + this.pad(this.pick(this.provinces({ country: 'it' })).code, 3); return it_vat + this.luhn_calculate(it_vat); }; /* * this generator is written following the official algorithm * all data can be passed explicitely or randomized by calling chance.cf() without options * the code does not check that the input data is valid (it goes beyond the scope of the generator) * * @param [Object] options = { first: first name, * last: last name, * gender: female|male, birthday: JavaScript date object, city: string(4), 1 letter + 3 numbers } * @return [string] codice fiscale * */ Chance.prototype.cf = function (options) { options = options || {}; var gender = !!options.gender ? options.gender : this.gender(), first = !!options.first ? options.first : this.first( { gender: gender, nationality: 'it'} ), last = !!options.last ? options.last : this.last( { nationality: 'it'} ), birthday = !!options.birthday ? options.birthday : this.birthday(), city = !!options.city ? options.city : this.pickone(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'L', 'M', 'Z']) + this.pad(this.natural({max:999}), 3), cf = [], name_generator = function(name, isLast) { var temp, return_value = []; if (name.length < 3) { return_value = name.split("").concat("XXX".split("")).splice(0,3); } else { temp = name.toUpperCase().split('').map(function(c){ return ("BCDFGHJKLMNPRSTVWZ".indexOf(c) !== -1) ? c : undefined; }).join(''); if (temp.length > 3) { if (isLast) { temp = temp.substr(0,3); } else { temp = temp[0] + temp.substr(2,2); } } if (temp.length < 3) { return_value = temp; temp = name.toUpperCase().split('').map(function(c){ return ("AEIOU".indexOf(c) !== -1) ? c : undefined; }).join('').substr(0, 3 - return_value.length); } return_value = return_value + temp; } return return_value; }, date_generator = function(birthday, gender, that) { var lettermonths = ['A', 'B', 'C', 'D', 'E', 'H', 'L', 'M', 'P', 'R', 'S', 'T']; return birthday.getFullYear().toString().substr(2) + lettermonths[birthday.getMonth()] + that.pad(birthday.getDate() + ((gender.toLowerCase() === "female") ? 40 : 0), 2); }, checkdigit_generator = function(cf) { var range1 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", range2 = "ABCDEFGHIJABCDEFGHIJKLMNOPQRSTUVWXYZ", evens = "ABCDEFGHIJKLMNOPQRSTUVWXYZ", odds = "BAKPLCQDREVOSFTGUHMINJWZYX", digit = 0; for(var i = 0; i < 15; i++) { if (i % 2 !== 0) { digit += evens.indexOf(range2[range1.indexOf(cf[i])]); } else { digit += odds.indexOf(range2[range1.indexOf(cf[i])]); } } return evens[digit % 26]; }; cf = cf.concat(name_generator(last, true), name_generator(first), date_generator(birthday, gender, this), city.toUpperCase().split("")).join(""); cf += checkdigit_generator(cf.toUpperCase(), this); return cf.toUpperCase(); }; Chance.prototype.pl_pesel = function () { var number = this.natural({min: 1, max: 9999999999}); var arr = this.pad(number, 10).split(''); for (var i = 0; i < arr.length; i++) { arr[i] = parseInt(arr[i]); } var controlNumber = (1 * arr[0] + 3 * arr[1] + 7 * arr[2] + 9 * arr[3] + 1 * arr[4] + 3 * arr[5] + 7 * arr[6] + 9 * arr[7] + 1 * arr[8] + 3 * arr[9]) % 10; if(controlNumber !== 0) { controlNumber = 10 - controlNumber; } return arr.join('') + controlNumber; }; Chance.prototype.pl_nip = function () { var number = this.natural({min: 1, max: 999999999}); var arr = this.pad(number, 9).split(''); for (var i = 0; i < arr.length; i++) { arr[i] = parseInt(arr[i]); } var controlNumber = (6 * arr[0] + 5 * arr[1] + 7 * arr[2] + 2 * arr[3] + 3 * arr[4] + 4 * arr[5] + 5 * arr[6] + 6 * arr[7] + 7 * arr[8]) % 11; if(controlNumber === 10) { return this.pl_nip(); } return arr.join('') + controlNumber; }; Chance.prototype.pl_regon = function () { var number = this.natural({min: 1, max: 99999999}); var arr = this.pad(number, 8).split(''); for (var i = 0; i < arr.length; i++) { arr[i] = parseInt(arr[i]); } var controlNumber = (8 * arr[0] + 9 * arr[1] + 2 * arr[2] + 3 * arr[3] + 4 * arr[4] + 5 * arr[5] + 6 * arr[6] + 7 * arr[7]) % 11; if(controlNumber === 10) { controlNumber = 0; } return arr.join('') + controlNumber; }; // -- End Regional // -- Miscellaneous -- // Dice - For all the board game geeks out there, myself included ;) function diceFn (range) { return function () { return this.natural(range); }; } Chance.prototype.d4 = diceFn({min: 1, max: 4}); Chance.prototype.d6 = diceFn({min: 1, max: 6}); Chance.prototype.d8 = diceFn({min: 1, max: 8}); Chance.prototype.d10 = diceFn({min: 1, max: 10}); Chance.prototype.d12 = diceFn({min: 1, max: 12}); Chance.prototype.d20 = diceFn({min: 1, max: 20}); Chance.prototype.d30 = diceFn({min: 1, max: 30}); Chance.prototype.d100 = diceFn({min: 1, max: 100}); Chance.prototype.rpg = function (thrown, options) { options = initOptions(options); if (!thrown) { throw new RangeError("Chance: A type of die roll must be included"); } else { var bits = thrown.toLowerCase().split("d"), rolls = []; if (bits.length !== 2 || !parseInt(bits[0], 10) || !parseInt(bits[1], 10)) { throw new Error("Chance: Invalid format provided. Please provide #d# where the first # is the number of dice to roll, the second # is the max of each die"); } for (var i = bits[0]; i > 0; i--) { rolls[i - 1] = this.natural({min: 1, max: bits[1]}); } return (typeof options.sum !== 'undefined' && options.sum) ? rolls.reduce(function (p, c) { return p + c; }) : rolls; } }; // Guid Chance.prototype.guid = function (options) { options = initOptions(options, { version: 5 }); var guid_pool = "abcdef1234567890", variant_pool = "ab89", guid = this.string({ pool: guid_pool, length: 8 }) + '-' + this.string({ pool: guid_pool, length: 4 }) + '-' + // The Version options.version + this.string({ pool: guid_pool, length: 3 }) + '-' + // The Variant this.string({ pool: variant_pool, length: 1 }) + this.string({ pool: guid_pool, length: 3 }) + '-' + this.string({ pool: guid_pool, length: 12 }); return guid; }; // Hash Chance.prototype.hash = function (options) { options = initOptions(options, {length : 40, casing: 'lower'}); var pool = options.casing === 'upper' ? HEX_POOL.toUpperCase() : HEX_POOL; return this.string({pool: pool, length: options.length}); }; Chance.prototype.luhn_check = function (num) { var str = num.toString(); var checkDigit = +str.substring(str.length - 1); return checkDigit === this.luhn_calculate(+str.substring(0, str.length - 1)); }; Chance.prototype.luhn_calculate = function (num) { var digits = num.toString().split("").reverse(); var sum = 0; var digit; for (var i = 0, l = digits.length; l > i; ++i) { digit = +digits[i]; if (i % 2 === 0) { digit *= 2; if (digit > 9) { digit -= 9; } } sum += digit; } return (sum * 9) % 10; }; // MD5 Hash Chance.prototype.md5 = function(options) { var opts = { str: '', key: null, raw: false }; if (!options) { opts.str = this.string(); options = {}; } else if (typeof options === 'string') { opts.str = options; options = {}; } else if (typeof options !== 'object') { return null; } else if(options.constructor === 'Array') { return null; } opts = initOptions(options, opts); if(!opts.str){ throw new Error('A parameter is required to return an md5 hash.'); } return this.bimd5.md5(opts.str, opts.key, opts.raw); }; /** * #Description: * ===================================================== * Generate random file name with extension * * The argument provide extension type * -> raster * -> vector * -> 3d * -> document * * If nothing is provided the function return random file name with random * extension type of any kind * * The user can validate the file name length range * If nothing provided the generated file name is random * * #Extension Pool : * * Currently the supported extensions are * -> some of the most popular raster image extensions * -> some of the most popular vector image extensions * -> some of the most popular 3d image extensions * -> some of the most popular document extensions * * #Examples : * ===================================================== * * Return random file name with random extension. The file extension * is provided by a predefined collection of extensions. More about the extension * pool can be found in #Extension Pool section * * chance.file() * => dsfsdhjf.xml * * In order to generate a file name with specific length, specify the * length property and integer value. The extension is going to be random * * chance.file({length : 10}) * => asrtineqos.pdf * * In order to generate file with extension from some of the predefined groups * of the extension pool just specify the extension pool category in fileType property * * chance.file({fileType : 'raster'}) * => dshgssds.psd * * You can provide specific extension for your files * chance.file({extension : 'html'}) * => djfsd.html * * Or you could pass custom collection of extensions by array or by object * chance.file({extensions : [...]}) * => dhgsdsd.psd * * chance.file({extensions : { key : [...], key : [...]}}) * => djsfksdjsd.xml * * @param [collection] options * @return [string] * */ Chance.prototype.file = function(options) { var fileOptions = options || {}; var poolCollectionKey = "fileExtension"; var typeRange = Object.keys(this.get("fileExtension"));//['raster', 'vector', '3d', 'document']; var fileName; var fileExtension; // Generate random file name fileName = this.word({length : fileOptions.length}); // Generate file by specific extension provided by the user if(fileOptions.extension) { fileExtension = fileOptions.extension; return (fileName + '.' + fileExtension); } // Generate file by specific extension collection if(fileOptions.extensions) { if(Array.isArray(fileOptions.extensions)) { fileExtension = this.pickone(fileOptions.extensions); return (fileName + '.' + fileExtension); } else if(fileOptions.extensions.constructor === Object) { var extensionObjectCollection = fileOptions.extensions; var keys = Object.keys(extensionObjectCollection); fileExtension = this.pickone(extensionObjectCollection[this.pickone(keys)]); return (fileName + '.' + fileExtension); } throw new Error("Chance: Extensions must be an Array or Object"); } // Generate file extension based on specific file type if(fileOptions.fileType) { var fileType = fileOptions.fileType; if(typeRange.indexOf(fileType) !== -1) { fileExtension = this.pickone(this.get(poolCollectionKey)[fileType]); return (fileName + '.' + fileExtension); } throw new RangeError("Chance: Expect file type value to be 'raster', 'vector', '3d' or 'document'"); } // Generate random file name if no extension options are passed fileExtension = this.pickone(this.get(poolCollectionKey)[this.pickone(typeRange)]); return (fileName + '.' + fileExtension); }; var data = { firstNames: { "male": { "en": ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Charles", "Thomas", "Christopher", "Daniel", "Matthew", "George", "Donald", "Anthony", "Paul", "Mark", "Edward", "Steven", "Kenneth", "Andrew", "Brian", "Joshua", "Kevin", "Ronald", "Timothy", "Jason", "Jeffrey", "Frank", "Gary", "Ryan", "Nicholas", "Eric", "Stephen", "Jacob", "Larry", "Jonathan", "Scott", "Raymond", "Justin", "Brandon", "Gregory", "Samuel", "Benjamin", "Patrick", "Jack", "Henry", "Walter", "Dennis", "Jerry", "Alexander", "Peter", "Tyler", "Douglas", "Harold", "Aaron", "Jose", "Adam", "Arthur", "Zachary", "Carl", "Nathan", "Albert", "Kyle", "Lawrence", "Joe", "Willie", "Gerald", "Roger", "Keith", "Jeremy", "Terry", "Harry", "Ralph", "Sean", "Jesse", "Roy", "Louis", "Billy", "Austin", "Bruce", "Eugene", "Christian", "Bryan", "Wayne", "Russell", "Howard", "Fred", "Ethan", "Jordan", "Philip", "Alan", "Juan", "Randy", "Vincent", "Bobby", "Dylan", "Johnny", "Phillip", "Victor", "Clarence", "Ernest", "Martin", "Craig", "Stanley", "Shawn", "Travis", "Bradley", "Leonard", "Earl", "Gabriel", "Jimmy", "Francis", "Todd", "Noah", "Danny", "Dale", "Cody", "Carlos", "Allen", "Frederick", "Logan", "Curtis", "Alex", "Joel", "Luis", "Norman", "Marvin", "Glenn", "Tony", "Nathaniel", "Rodney", "Melvin", "Alfred", "Steve", "Cameron", "Chad", "Edwin", "Caleb", "Evan", "Antonio", "Lee", "Herbert", "Jeffery", "Isaac", "Derek", "Ricky", "Marcus", "Theodore", "Elijah", "Luke", "Jesus", "Eddie", "Troy", "Mike", "Dustin", "Ray", "Adrian", "Bernard", "Leroy", "Angel", "Randall", "Wesley", "Ian", "Jared", "Mason", "Hunter", "Calvin", "Oscar", "Clifford", "Jay", "Shane", "Ronnie", "Barry", "Lucas", "Corey", "Manuel", "Leo", "Tommy", "Warren", "Jackson", "Isaiah", "Connor", "Don", "Dean", "Jon", "Julian", "Miguel", "Bill", "Lloyd", "Charlie", "Mitchell", "Leon", "Jerome", "Darrell", "Jeremiah", "Alvin", "Brett", "Seth", "Floyd", "Jim", "Blake", "Micheal", "Gordon", "Trevor", "Lewis", "Erik", "Edgar", "Vernon", "Devin", "Gavin", "Jayden", "Chris", "Clyde", "Tom", "Derrick", "Mario", "Brent", "Marc", "Herman", "Chase", "Dominic", "Ricardo", "Franklin", "Maurice", "Max", "Aiden", "Owen", "Lester", "Gilbert", "Elmer", "Gene", "Francisco", "Glen", "Cory", "Garrett", "Clayton", "Sam", "Jorge", "Chester", "Alejandro", "Jeff", "Harvey", "Milton", "Cole", "Ivan", "Andre", "Duane", "Landon"], // Data taken from http://www.dati.gov.it/dataset/comune-di-firenze_0163 "it": ["Adolfo", "Alberto", "Aldo", "Alessandro", "Alessio", "Alfredo", "Alvaro", "Andrea", "Angelo", "Angiolo", "Antonino", "Antonio", "Attilio", "Benito", "Bernardo", "Bruno", "Carlo", "Cesare", "Christian", "Claudio", "Corrado", "Cosimo", "Cristian", "Cristiano", "Daniele", "Dario", "David", "Davide", "Diego", "Dino", "Domenico", "Duccio", "Edoardo", "Elia", "Elio", "Emanuele", "Emiliano", "Emilio", "Enrico", "Enzo", "Ettore", "Fabio", "Fabrizio", "Federico", "Ferdinando", "Fernando", "Filippo", "Francesco", "Franco", "Gabriele", "Giacomo", "Giampaolo", "Giampiero", "Giancarlo", "Gianfranco", "Gianluca", "Gianmarco", "Gianni", "Gino", "Giorgio", "Giovanni", "Giuliano", "Giulio", "Giuseppe", "Graziano", "Gregorio", "Guido", "Iacopo", "Jacopo", "Lapo", "Leonardo", "Lorenzo", "Luca", "Luciano", "Luigi", "Manuel", "Marcello", "Marco", "Marino", "Mario", "Massimiliano", "Massimo", "Matteo", "Mattia", "Maurizio", "Mauro", "Michele", "Mirko", "Mohamed", "Nello", "Neri", "Niccolò", "Nicola", "Osvaldo", "Otello", "Paolo", "Pier Luigi", "Piero", "Pietro", "Raffaele", "Remo", "Renato", "Renzo", "Riccardo", "Roberto", "Rolando", "Romano", "Salvatore", "Samuele", "Sandro", "Sergio", "Silvano", "Simone", "Stefano", "Thomas", "Tommaso", "Ubaldo", "Ugo", "Umberto", "Valerio", "Valter", "Vasco", "Vincenzo", "Vittorio"], // Data taken from http://www.svbkindernamen.nl/int/nl/kindernamen/index.html "nl": ["Aaron","Abel","Adam","Adriaan","Albert","Alexander","Ali","Arjen","Arno","Bart","Bas","Bastiaan","Benjamin","Bob", "Boris","Bram","Brent","Cas","Casper","Chris","Christiaan","Cornelis","Daan","Daley","Damian","Dani","Daniel","Daniël","David","Dean","Dirk","Dylan","Egbert","Elijah","Erik","Erwin","Evert","Ezra","Fabian","Fedde","Finn","Florian","Floris","Frank","Frans","Frederik","Freek","Geert","Gerard","Gerben","Gerrit","Gijs","Guus","Hans","Hendrik","Henk","Herman","Hidde","Hugo","Jaap","Jan Jaap","Jan-Willem","Jack","Jacob","Jan","Jason","Jasper","Jayden","Jelle","Jelte","Jens","Jeroen","Jesse","Jim","Job","Joep","Johannes","John","Jonathan","Joris","Joshua","Joël","Julian","Kees","Kevin","Koen","Lars","Laurens","Leendert","Lennard","Lodewijk","Luc","Luca","Lucas","Lukas","Luuk","Maarten","Marcus","Martijn","Martin","Matthijs","Maurits","Max","Mees","Melle","Mick","Mika","Milan","Mohamed","Mohammed","Morris","Muhammed","Nathan","Nick","Nico","Niek","Niels","Noah","Noud","Olivier","Oscar","Owen","Paul","Pepijn","Peter","Pieter","Pim","Quinten","Reinier","Rens","Robin","Ruben","Sam","Samuel","Sander","Sebastiaan","Sem","Sep","Sepp","Siem","Simon","Stan","Stef","Steven","Stijn","Sven","Teun","Thijmen","Thijs","Thomas","Tijn","Tim","Timo","Tobias","Tom","Victor","Vince","Willem","Wim","Wouter","Yusuf"] }, "female": { "en": ["Mary", "Emma", "Elizabeth", "Minnie", "Margaret", "Ida", "Alice", "Bertha", "Sarah", "Annie", "Clara", "Ella", "Florence", "Cora", "Martha", "Laura", "Nellie", "Grace", "Carrie", "Maude", "Mabel", "Bessie", "Jennie", "Gertrude", "Julia", "Hattie", "Edith", "Mattie", "Rose", "Catherine", "Lillian", "Ada", "Lillie", "Helen", "Jessie", "Louise", "Ethel", "Lula", "Myrtle", "Eva", "Frances", "Lena", "Lucy", "Edna", "Maggie", "Pearl", "Daisy", "Fannie", "Josephine", "Dora", "Rosa", "Katherine", "Agnes", "Marie", "Nora", "May", "Mamie", "Blanche", "Stella", "Ellen", "Nancy", "Effie", "Sallie", "Nettie", "Della", "Lizzie", "Flora", "Susie", "Maud", "Mae", "Etta", "Harriet", "Sadie", "Caroline", "Katie", "Lydia", "Elsie", "Kate", "Susan", "Mollie", "Alma", "Addie", "Georgia", "Eliza", "Lulu", "Nannie", "Lottie", "Amanda", "Belle", "Charlotte", "Rebecca", "Ruth", "Viola", "Olive", "Amelia", "Hannah", "Jane", "Virginia", "Emily", "Matilda", "Irene", "Kathryn", "Esther", "Willie", "Henrietta", "Ollie", "Amy", "Rachel", "Sara", "Estella", "Theresa", "Augusta", "Ora", "Pauline", "Josie", "Lola", "Sophia", "Leona", "Anne", "Mildred", "Ann", "Beulah", "Callie", "Lou", "Delia", "Eleanor", "Barbara", "Iva", "Louisa", "Maria", "Mayme", "Evelyn", "Estelle", "Nina", "Betty", "Marion", "Bettie", "Dorothy", "Luella", "Inez", "Lela", "Rosie", "Allie", "Millie", "Janie", "Cornelia", "Victoria", "Ruby", "Winifred", "Alta", "Celia", "Christine", "Beatrice", "Birdie", "Harriett", "Mable", "Myra", "Sophie", "Tillie", "Isabel", "Sylvia", "Carolyn", "Isabelle", "Leila", "Sally", "Ina", "Essie", "Bertie", "Nell", "Alberta", "Katharine", "Lora", "Rena", "Mina", "Rhoda", "Mathilda", "Abbie", "Eula", "Dollie", "Hettie", "Eunice", "Fanny", "Ola", "Lenora", "Adelaide", "Christina", "Lelia", "Nelle", "Sue", "Johanna", "Lilly", "Lucinda", "Minerva", "Lettie", "Roxie", "Cynthia", "Helena", "Hilda", "Hulda", "Bernice", "Genevieve", "Jean", "Cordelia", "Marian", "Francis", "Jeanette", "Adeline", "Gussie", "Leah", "Lois", "Lura", "Mittie", "Hallie", "Isabella", "Olga", "Phoebe", "Teresa", "Hester", "Lida", "Lina", "Winnie", "Claudia", "Marguerite", "Vera", "Cecelia", "Bess", "Emilie", "Rosetta", "Verna", "Myrtie", "Cecilia", "Elva", "Olivia", "Ophelia", "Georgie", "Elnora", "Violet", "Adele", "Lily", "Linnie", "Loretta", "Madge", "Polly", "Virgie", "Eugenia", "Lucile", "Lucille", "Mabelle", "Rosalie"], // Data taken from http://www.dati.gov.it/dataset/comune-di-firenze_0162 "it": ["Ada", "Adriana", "Alessandra", "Alessia", "Alice", "Angela", "Anna", "Anna Maria", "Annalisa", "Annita", "Annunziata", "Antonella", "Arianna", "Asia", "Assunta", "Aurora", "Barbara", "Beatrice", "Benedetta", "Bianca", "Bruna", "Camilla", "Carla", "Carlotta", "Carmela", "Carolina", "Caterina", "Catia", "Cecilia", "Chiara", "Cinzia", "Clara", "Claudia", "Costanza", "Cristina", "Daniela", "Debora", "Diletta", "Dina", "Donatella", "Elena", "Eleonora", "Elisa", "Elisabetta", "Emanuela", "Emma", "Eva", "Federica", "Fernanda", "Fiorella", "Fiorenza", "Flora", "Franca", "Francesca", "Gabriella", "Gaia", "Gemma", "Giada", "Gianna", "Gina", "Ginevra", "Giorgia", "Giovanna", "Giulia", "Giuliana", "Giuseppa", "Giuseppina", "Grazia", "Graziella", "Greta", "Ida", "Ilaria", "Ines", "Iolanda", "Irene", "Irma", "Isabella", "Jessica", "Laura", "Lea", "Letizia", "Licia", "Lidia", "Liliana", "Lina", "Linda", "Lisa", "Livia", "Loretta", "Luana", "Lucia", "Luciana", "Lucrezia", "Luisa", "Manuela", "Mara", "Marcella", "Margherita", "Maria", "Maria Cristina", "Maria Grazia", "Maria Luisa", "Maria Pia", "Maria Teresa", "Marina", "Marisa", "Marta", "Martina", "Marzia", "Matilde", "Melissa", "Michela", "Milena", "Mirella", "Monica", "Natalina", "Nella", "Nicoletta", "Noemi", "Olga", "Paola", "Patrizia", "Piera", "Pierina", "Raffaella", "Rebecca", "Renata", "Rina", "Rita", "Roberta", "Rosa", "Rosanna", "Rossana", "Rossella", "Sabrina", "Sandra", "Sara", "Serena", "Silvana", "Silvia", "Simona", "Simonetta", "Sofia", "Sonia", "Stefania", "Susanna", "Teresa", "Tina", "Tiziana", "Tosca", "Valentina", "Valeria", "Vanda", "Vanessa", "Vanna", "Vera", "Veronica", "Vilma", "Viola", "Virginia", "Vittoria"], // Data taken from http://www.svbkindernamen.nl/int/nl/kindernamen/index.html "nl": ["Ada", "Arianne", "Afke", "Amanda", "Amber", "Amy", "Aniek", "Anita", "Anja", "Anna", "Anne", "Annelies", "Annemarie", "Annette", "Anouk", "Astrid", "Aukje", "Barbara", "Bianca", "Carla", "Carlijn", "Carolien", "Chantal", "Charlotte", "Claudia", "Daniëlle", "Debora", "Diane", "Dora", "Eline", "Elise", "Ella", "Ellen", "Emma", "Esmee", "Evelien", "Esther", "Erica", "Eva", "Femke", "Fleur", "Floor", "Froukje", "Gea", "Gerda", "Hanna", "Hanneke", "Heleen", "Hilde", "Ilona", "Ina", "Inge", "Ingrid", "Iris", "Isabel", "Isabelle", "Janneke", "Jasmijn", "Jeanine", "Jennifer", "Jessica", "Johanna", "Joke", "Julia", "Julie", "Karen", "Karin", "Katja", "Kim", "Lara", "Laura", "Lena", "Lianne", "Lieke", "Lilian", "Linda", "Lisa", "Lisanne", "Lotte", "Louise", "Maaike", "Manon", "Marga", "Maria", "Marissa", "Marit", "Marjolein", "Martine", "Marleen", "Melissa", "Merel", "Miranda", "Michelle", "Mirjam", "Mirthe", "Naomi", "Natalie", 'Nienke', "Nina", "Noortje", "Olivia", "Patricia", "Paula", "Paulien", "Ramona", "Ria", "Rianne", "Roos", "Rosanne", "Ruth", "Sabrina", "Sandra", "Sanne", "Sara", "Saskia", "Silvia", "Sofia", "Sophie", "Sonja", "Suzanne", "Tamara", "Tess", "Tessa", "Tineke", "Valerie", "Vanessa", "Veerle", "Vera", "Victoria", "Wendy", "Willeke", "Yvonne", "Zoë"] } }, lastNames: { "en": ['Smith', 'Johnson', 'Williams', 'Jones', 'Brown', 'Davis', 'Miller', 'Wilson', 'Moore', 'Taylor', 'Anderson', 'Thomas', 'Jackson', 'White', 'Harris', 'Martin', 'Thompson', 'Garcia', 'Martinez', 'Robinson', 'Clark', 'Rodriguez', 'Lewis', 'Lee', 'Walker', 'Hall', 'Allen', 'Young', 'Hernandez', 'King', 'Wright', 'Lopez', 'Hill', 'Scott', 'Green', 'Adams', 'Baker', 'Gonzalez', 'Nelson', 'Carter', 'Mitchell', 'Perez', 'Roberts', 'Turner', 'Phillips', 'Campbell', 'Parker', 'Evans', 'Edwards', 'Collins', 'Stewart', 'Sanchez', 'Morris', 'Rogers', 'Reed', 'Cook', 'Morgan', 'Bell', 'Murphy', 'Bailey', 'Rivera', 'Cooper', 'Richardson', 'Cox', 'Howard', 'Ward', 'Torres', 'Peterson', 'Gray', 'Ramirez', 'James', 'Watson', 'Brooks', 'Kelly', 'Sanders', 'Price', 'Bennett', 'Wood', 'Barnes', 'Ross', 'Henderson', 'Coleman', 'Jenkins', 'Perry', 'Powell', 'Long', 'Patterson', 'Hughes', 'Flores', 'Washington', 'Butler', 'Simmons', 'Foster', 'Gonzales', 'Bryant', 'Alexander', 'Russell', 'Griffin', 'Diaz', 'Hayes', 'Myers', 'Ford', 'Hamilton', 'Graham', 'Sullivan', 'Wallace', 'Woods', 'Cole', 'West', 'Jordan', 'Owens', 'Reynolds', 'Fisher', 'Ellis', 'Harrison', 'Gibson', 'McDonald', 'Cruz', 'Marshall', 'Ortiz', 'Gomez', 'Murray', 'Freeman', 'Wells', 'Webb', 'Simpson', 'Stevens', 'Tucker', 'Porter', 'Hunter', 'Hicks', 'Crawford', 'Henry', 'Boyd', 'Mason', 'Morales', 'Kennedy', 'Warren', 'Dixon', 'Ramos', 'Reyes', 'Burns', 'Gordon', 'Shaw', 'Holmes', 'Rice', 'Robertson', 'Hunt', 'Black', 'Daniels', 'Palmer', 'Mills', 'Nichols', 'Grant', 'Knight', 'Ferguson', 'Rose', 'Stone', 'Hawkins', 'Dunn', 'Perkins', 'Hudson', 'Spencer', 'Gardner', 'Stephens', 'Payne', 'Pierce', 'Berry', 'Matthews', 'Arnold', 'Wagner', 'Willis', 'Ray', 'Watkins', 'Olson', 'Carroll', 'Duncan', 'Snyder', 'Hart', 'Cunningham', 'Bradley', 'Lane', 'Andrews', 'Ruiz', 'Harper', 'Fox', 'Riley', 'Armstrong', 'Carpenter', 'Weaver', 'Greene', 'Lawrence', 'Elliott', 'Chavez', 'Sims', 'Austin', 'Peters', 'Kelley', 'Franklin', 'Lawson', 'Fields', 'Gutierrez', 'Ryan', 'Schmidt', 'Carr', 'Vasquez', 'Castillo', 'Wheeler', 'Chapman', 'Oliver', 'Montgomery', 'Richards', 'Williamson', 'Johnston', 'Banks', 'Meyer', 'Bishop', 'McCoy', 'Howell', 'Alvarez', 'Morrison', 'Hansen', 'Fernandez', 'Garza', 'Harvey', 'Little', 'Burton', 'Stanley', 'Nguyen', 'George', 'Jacobs', 'Reid', 'Kim', 'Fuller', 'Lynch', 'Dean', 'Gilbert', 'Garrett', 'Romero', 'Welch', 'Larson', 'Frazier', 'Burke', 'Hanson', 'Day', 'Mendoza', 'Moreno', 'Bowman', 'Medina', 'Fowler', 'Brewer', 'Hoffman', 'Carlson', 'Silva', 'Pearson', 'Holland', 'Douglas', 'Fleming', 'Jensen', 'Vargas', 'Byrd', 'Davidson', 'Hopkins', 'May', 'Terry', 'Herrera', 'Wade', 'Soto', 'Walters', 'Curtis', 'Neal', 'Caldwell', 'Lowe', 'Jennings', 'Barnett', 'Graves', 'Jimenez', 'Horton', 'Shelton', 'Barrett', 'Obrien', 'Castro', 'Sutton', 'Gregory', 'McKinney', 'Lucas', 'Miles', 'Craig', 'Rodriquez', 'Chambers', 'Holt', 'Lambert', 'Fletcher', 'Watts', 'Bates', 'Hale', 'Rhodes', 'Pena', 'Beck', 'Newman', 'Haynes', 'McDaniel', 'Mendez', 'Bush', 'Vaughn', 'Parks', 'Dawson', 'Santiago', 'Norris', 'Hardy', 'Love', 'Steele', 'Curry', 'Powers', 'Schultz', 'Barker', 'Guzman', 'Page', 'Munoz', 'Ball', 'Keller', 'Chandler', 'Weber', 'Leonard', 'Walsh', 'Lyons', 'Ramsey', 'Wolfe', 'Schneider', 'Mullins', 'Benson', 'Sharp', 'Bowen', 'Daniel', 'Barber', 'Cummings', 'Hines', 'Baldwin', 'Griffith', 'Valdez', 'Hubbard', 'Salazar', 'Reeves', 'Warner', 'Stevenson', 'Burgess', 'Santos', 'Tate', 'Cross', 'Garner', 'Mann', 'Mack', 'Moss', 'Thornton', 'Dennis', 'McGee', 'Farmer', 'Delgado', 'Aguilar', 'Vega', 'Glover', 'Manning', 'Cohen', 'Harmon', 'Rodgers', 'Robbins', 'Newton', 'Todd', 'Blair', 'Higgins', 'Ingram', 'Reese', 'Cannon', 'Strickland', 'Townsend', 'Potter', 'Goodwin', 'Walton', 'Rowe', 'Hampton', 'Ortega', 'Patton', 'Swanson', 'Joseph', 'Francis', 'Goodman', 'Maldonado', 'Yates', 'Becker', 'Erickson', 'Hodges', 'Rios', 'Conner', 'Adkins', 'Webster', 'Norman', 'Malone', 'Hammond', 'Flowers', 'Cobb', 'Moody', 'Quinn', 'Blake', 'Maxwell', 'Pope', 'Floyd', 'Osborne', 'Paul', 'McCarthy', 'Guerrero', 'Lindsey', 'Estrada', 'Sandoval', 'Gibbs', 'Tyler', 'Gross', 'Fitzgerald', 'Stokes', 'Doyle', 'Sherman', 'Saunders', 'Wise', 'Colon', 'Gill', 'Alvarado', 'Greer', 'Padilla', 'Simon', 'Waters', 'Nunez', 'Ballard', 'Schwartz', 'McBride', 'Houston', 'Christensen', 'Klein', 'Pratt', 'Briggs', 'Parsons', 'McLaughlin', 'Zimmerman', 'French', 'Buchanan', 'Moran', 'Copeland', 'Roy', 'Pittman', 'Brady', 'McCormick', 'Holloway', 'Brock', 'Poole', 'Frank', 'Logan', 'Owen', 'Bass', 'Marsh', 'Drake', 'Wong', 'Jefferson', 'Park', 'Morton', 'Abbott', 'Sparks', 'Patrick', 'Norton', 'Huff', 'Clayton', 'Massey', 'Lloyd', 'Figueroa', 'Carson', 'Bowers', 'Roberson', 'Barton', 'Tran', 'Lamb', 'Harrington', 'Casey', 'Boone', 'Cortez', 'Clarke', 'Mathis', 'Singleton', 'Wilkins', 'Cain', 'Bryan', 'Underwood', 'Hogan', 'McKenzie', 'Collier', 'Luna', 'Phelps', 'McGuire', 'Allison', 'Bridges', 'Wilkerson', 'Nash', 'Summers', 'Atkins'], // Data taken from http://www.dati.gov.it/dataset/comune-di-firenze_0164 (first 1000) "it": ["Acciai", "Aglietti", "Agostini", "Agresti", "Ahmed", "Aiazzi", "Albanese", "Alberti", "Alessi", "Alfani", "Alinari", "Alterini", "Amato", "Ammannati", "Ancillotti", "Andrei", "Andreini", "Andreoni", "Angeli", "Anichini", "Antonelli", "Antonini", "Arena", "Ariani", "Arnetoli", "Arrighi", "Baccani", "Baccetti", "Bacci", "Bacherini", "Badii", "Baggiani", "Baglioni", "Bagni", "Bagnoli", "Baldassini", "Baldi", "Baldini", "Ballerini", "Balli", "Ballini", "Balloni", "Bambi", "Banchi", "Bandinelli", "Bandini", "Bani", "Barbetti", "Barbieri", "Barchielli", "Bardazzi", "Bardelli", "Bardi", "Barducci", "Bargellini", "Bargiacchi", "Barni", "Baroncelli", "Baroncini", "Barone", "Baroni", "Baronti", "Bartalesi", "Bartoletti", "Bartoli", "Bartolini", "Bartoloni", "Bartolozzi", "Basagni", "Basile", "Bassi", "Batacchi", "Battaglia", "Battaglini", "Bausi", "Becagli", "Becattini", "Becchi", "Becucci", "Bellandi", "Bellesi", "Belli", "Bellini", "Bellucci", "Bencini", "Benedetti", "Benelli", "Beni", "Benini", "Bensi", "Benucci", "Benvenuti", "Berlincioni", "Bernacchioni", "Bernardi", "Bernardini", "Berni", "Bernini", "Bertelli", "Berti", "Bertini", "Bessi", "Betti", "Bettini", "Biagi", "Biagini", "Biagioni", "Biagiotti", "Biancalani", "Bianchi", "Bianchini", "Bianco", "Biffoli", "Bigazzi", "Bigi", "Biliotti", "Billi", "Binazzi", "Bindi", "Bini", "Biondi", "Bizzarri", "Bocci", "Bogani", "Bolognesi", "Bonaiuti", "Bonanni", "Bonciani", "Boncinelli", "Bondi", "Bonechi", "Bongini", "Boni", "Bonini", "Borchi", "Boretti", "Borghi", "Borghini", "Borgioli", "Borri", "Borselli", "Boschi", "Bottai", "Bracci", "Braccini", "Brandi", "Braschi", "Bravi", "Brazzini", "Breschi", "Brilli", "Brizzi", "Brogelli", "Brogi", "Brogioni", "Brunelli", "Brunetti", "Bruni", "Bruno", "Brunori", "Bruschi", "Bucci", "Bucciarelli", "Buccioni", "Bucelli", "Bulli", "Burberi", "Burchi", "Burgassi", "Burroni", "Bussotti", "Buti", "Caciolli", "Caiani", "Calabrese", "Calamai", "Calamandrei", "Caldini", "Calo'", "Calonaci", "Calosi", "Calvelli", "Cambi", "Camiciottoli", "Cammelli", "Cammilli", "Campolmi", "Cantini", "Capanni", "Capecchi", "Caponi", "Cappelletti", "Cappelli", "Cappellini", "Cappugi", "Capretti", "Caputo", "Carbone", "Carboni", "Cardini", "Carlesi", "Carletti", "Carli", "Caroti", "Carotti", "Carrai", "Carraresi", "Carta", "Caruso", "Casalini", "Casati", "Caselli", "Casini", "Castagnoli", "Castellani", "Castelli", "Castellucci", "Catalano", "Catarzi", "Catelani", "Cavaciocchi", "Cavallaro", "Cavallini", "Cavicchi", "Cavini", "Ceccarelli", "Ceccatelli", "Ceccherelli", "Ceccherini", "Cecchi", "Cecchini", "Cecconi", "Cei", "Cellai", "Celli", "Cellini", "Cencetti", "Ceni", "Cenni", "Cerbai", "Cesari", "Ceseri", "Checcacci", "Checchi", "Checcucci", "Cheli", "Chellini", "Chen", "Cheng", "Cherici", "Cherubini", "Chiaramonti", "Chiarantini", "Chiarelli", "Chiari", "Chiarini", "Chiarugi", "Chiavacci", "Chiesi", "Chimenti", "Chini", "Chirici", "Chiti", "Ciabatti", "Ciampi", "Cianchi", "Cianfanelli", "Cianferoni", "Ciani", "Ciapetti", "Ciappi", "Ciardi", "Ciatti", "Cicali", "Ciccone", "Cinelli", "Cini", "Ciobanu", "Ciolli", "Cioni", "Cipriani", "Cirillo", "Cirri", "Ciucchi", "Ciuffi", "Ciulli", "Ciullini", "Clemente", "Cocchi", "Cognome", "Coli", "Collini", "Colombo", "Colzi", "Comparini", "Conforti", "Consigli", "Conte", "Conti", "Contini", "Coppini", "Coppola", "Corsi", "Corsini", "Corti", "Cortini", "Cosi", "Costa", "Costantini", "Costantino", "Cozzi", "Cresci", "Crescioli", "Cresti", "Crini", "Curradi", "D'Agostino", "D'Alessandro", "D'Amico", "D'Angelo", "Daddi", "Dainelli", "Dallai", "Danti", "Davitti", "De Angelis", "De Luca", "De Marco", "De Rosa", "De Santis", "De Simone", "De Vita", "Degl'Innocenti", "Degli Innocenti", "Dei", "Del Lungo", "Del Re", "Di Marco", "Di Stefano", "Dini", "Diop", "Dobre", "Dolfi", "Donati", "Dondoli", "Dong", "Donnini", "Ducci", "Dumitru", "Ermini", "Esposito", "Evangelisti", "Fabbri", "Fabbrini", "Fabbrizzi", "Fabbroni", "Fabbrucci", "Fabiani", "Facchini", "Faggi", "Fagioli", "Failli", "Faini", "Falciani", "Falcini", "Falcone", "Fallani", "Falorni", "Falsini", "Falugiani", "Fancelli", "Fanelli", "Fanetti", "Fanfani", "Fani", "Fantappie'", "Fantechi", "Fanti", "Fantini", "Fantoni", "Farina", "Fattori", "Favilli", "Fedi", "Fei", "Ferrante", "Ferrara", "Ferrari", "Ferraro", "Ferretti", "Ferri", "Ferrini", "Ferroni", "Fiaschi", "Fibbi", "Fiesoli", "Filippi", "Filippini", "Fini", "Fioravanti", "Fiore", "Fiorentini", "Fiorini", "Fissi", "Focardi", "Foggi", "Fontana", "Fontanelli", "Fontani", "Forconi", "Formigli", "Forte", "Forti", "Fortini", "Fossati", "Fossi", "Francalanci", "Franceschi", "Franceschini", "Franchi", "Franchini", "Franci", "Francini", "Francioni", "Franco", "Frassineti", "Frati", "Fratini", "Frilli", "Frizzi", "Frosali", "Frosini", "Frullini", "Fusco", "Fusi", "Gabbrielli", "Gabellini", "Gagliardi", "Galanti", "Galardi", "Galeotti", "Galletti", "Galli", "Gallo", "Gallori", "Gambacciani", "Gargani", "Garofalo", "Garuglieri", "Gashi", "Gasperini", "Gatti", "Gelli", "Gensini", "Gentile", "Gentili", "Geri", "Gerini", "Gheri", "Ghini", "Giachetti", "Giachi", "Giacomelli", "Gianassi", "Giani", "Giannelli", "Giannetti", "Gianni", "Giannini", "Giannoni", "Giannotti", "Giannozzi", "Gigli", "Giordano", "Giorgetti", "Giorgi", "Giovacchini", "Giovannelli", "Giovannetti", "Giovannini", "Giovannoni", "Giuliani", "Giunti", "Giuntini", "Giusti", "Gonnelli", "Goretti", "Gori", "Gradi", "Gramigni", "Grassi", "Grasso", "Graziani", "Grazzini", "Greco", "Grifoni", "Grillo", "Grimaldi", "Grossi", "Gualtieri", "Guarducci", "Guarino", "Guarnieri", "Guasti", "Guerra", "Guerri", "Guerrini", "Guidi", "Guidotti", "He", "Hoxha", "Hu", "Huang", "Iandelli", "Ignesti", "Innocenti", "Jin", "La Rosa", "Lai", "Landi", "Landini", "Lanini", "Lapi", "Lapini", "Lari", "Lascialfari", "Lastrucci", "Latini", "Lazzeri", "Lazzerini", "Lelli", "Lenzi", "Leonardi", "Leoncini", "Leone", "Leoni", "Lepri", "Li", "Liao", "Lin", "Linari", "Lippi", "Lisi", "Livi", "Lombardi", "Lombardini", "Lombardo", "Longo", "Lopez", "Lorenzi", "Lorenzini", "Lorini", "Lotti", "Lu", "Lucchesi", "Lucherini", "Lunghi", "Lupi", "Madiai", "Maestrini", "Maffei", "Maggi", "Maggini", "Magherini", "Magini", "Magnani", "Magnelli", "Magni", "Magnolfi", "Magrini", "Malavolti", "Malevolti", "Manca", "Mancini", "Manetti", "Manfredi", "Mangani", "Mannelli", "Manni", "Mannini", "Mannucci", "Manuelli", "Manzini", "Marcelli", "Marchese", "Marchetti", "Marchi", "Marchiani", "Marchionni", "Marconi", "Marcucci", "Margheri", "Mari", "Mariani", "Marilli", "Marinai", "Marinari", "Marinelli", "Marini", "Marino", "Mariotti", "Marsili", "Martelli", "Martinelli", "Martini", "Martino", "Marzi", "Masi", "Masini", "Masoni", "Massai", "Materassi", "Mattei", "Matteini", "Matteucci", "Matteuzzi", "Mattioli", "Mattolini", "Matucci", "Mauro", "Mazzanti", "Mazzei", "Mazzetti", "Mazzi", "Mazzini", "Mazzocchi", "Mazzoli", "Mazzoni", "Mazzuoli", "Meacci", "Mecocci", "Meini", "Melani", "Mele", "Meli", "Mengoni", "Menichetti", "Meoni", "Merlini", "Messeri", "Messina", "Meucci", "Miccinesi", "Miceli", "Micheli", "Michelini", "Michelozzi", "Migliori", "Migliorini", "Milani", "Miniati", "Misuri", "Monaco", "Montagnani", "Montagni", "Montanari", "Montelatici", "Monti", "Montigiani", "Montini", "Morandi", "Morandini", "Morelli", "Moretti", "Morganti", "Mori", "Morini", "Moroni", "Morozzi", "Mugnai", "Mugnaini", "Mustafa", "Naldi", "Naldini", "Nannelli", "Nanni", "Nannini", "Nannucci", "Nardi", "Nardini", "Nardoni", "Natali", "Ndiaye", "Nencetti", "Nencini", "Nencioni", "Neri", "Nesi", "Nesti", "Niccolai", "Niccoli", "Niccolini", "Nigi", "Nistri", "Nocentini", "Noferini", "Novelli", "Nucci", "Nuti", "Nutini", "Oliva", "Olivieri", "Olmi", "Orlandi", "Orlandini", "Orlando", "Orsini", "Ortolani", "Ottanelli", "Pacciani", "Pace", "Paci", "Pacini", "Pagani", "Pagano", "Paggetti", "Pagliai", "Pagni", "Pagnini", "Paladini", "Palagi", "Palchetti", "Palloni", "Palmieri", "Palumbo", "Pampaloni", "Pancani", "Pandolfi", "Pandolfini", "Panerai", "Panichi", "Paoletti", "Paoli", "Paolini", "Papi", "Papini", "Papucci", "Parenti", "Parigi", "Parisi", "Parri", "Parrini", "Pasquini", "Passeri", "Pecchioli", "Pecorini", "Pellegrini", "Pepi", "Perini", "Perrone", "Peruzzi", "Pesci", "Pestelli", "Petri", "Petrini", "Petrucci", "Pettini", "Pezzati", "Pezzatini", "Piani", "Piazza", "Piazzesi", "Piazzini", "Piccardi", "Picchi", "Piccini", "Piccioli", "Pieraccini", "Pieraccioni", "Pieralli", "Pierattini", "Pieri", "Pierini", "Pieroni", "Pietrini", "Pini", "Pinna", "Pinto", "Pinzani", "Pinzauti", "Piras", "Pisani", "Pistolesi", "Poggesi", "Poggi", "Poggiali", "Poggiolini", "Poli", "Pollastri", "Porciani", "Pozzi", "Pratellesi", "Pratesi", "Prosperi", "Pruneti", "Pucci", "Puccini", "Puccioni", "Pugi", "Pugliese", "Puliti", "Querci", "Quercioli", "Raddi", "Radu", "Raffaelli", "Ragazzini", "Ranfagni", "Ranieri", "Rastrelli", "Raugei", "Raveggi", "Renai", "Renzi", "Rettori", "Ricci", "Ricciardi", "Ridi", "Ridolfi", "Rigacci", "Righi", "Righini", "Rinaldi", "Risaliti", "Ristori", "Rizzo", "Rocchi", "Rocchini", "Rogai", "Romagnoli", "Romanelli", "Romani", "Romano", "Romei", "Romeo", "Romiti", "Romoli", "Romolini", "Rontini", "Rosati", "Roselli", "Rosi", "Rossetti", "Rossi", "Rossini", "Rovai", "Ruggeri", "Ruggiero", "Russo", "Sabatini", "Saccardi", "Sacchetti", "Sacchi", "Sacco", "Salerno", "Salimbeni", "Salucci", "Salvadori", "Salvestrini", "Salvi", "Salvini", "Sanesi", "Sani", "Sanna", "Santi", "Santini", "Santoni", "Santoro", "Santucci", "Sardi", "Sarri", "Sarti", "Sassi", "Sbolci", "Scali", "Scarpelli", "Scarselli", "Scopetani", "Secci", "Selvi", "Senatori", "Senesi", "Serafini", "Sereni", "Serra", "Sestini", "Sguanci", "Sieni", "Signorini", "Silvestri", "Simoncini", "Simonetti", "Simoni", "Singh", "Sodi", "Soldi", "Somigli", "Sorbi", "Sorelli", "Sorrentino", "Sottili", "Spina", "Spinelli", "Staccioli", "Staderini", "Stefanelli", "Stefani", "Stefanini", "Stella", "Susini", "Tacchi", "Tacconi", "Taddei", "Tagliaferri", "Tamburini", "Tanganelli", "Tani", "Tanini", "Tapinassi", "Tarchi", "Tarchiani", "Targioni", "Tassi", "Tassini", "Tempesti", "Terzani", "Tesi", "Testa", "Testi", "Tilli", "Tinti", "Tirinnanzi", "Toccafondi", "Tofanari", "Tofani", "Tognaccini", "Tonelli", "Tonini", "Torelli", "Torrini", "Tosi", "Toti", "Tozzi", "Trambusti", "Trapani", "Tucci", "Turchi", "Ugolini", "Ulivi", "Valente", "Valenti", "Valentini", "Vangelisti", "Vanni", "Vannini", "Vannoni", "Vannozzi", "Vannucchi", "Vannucci", "Ventura", "Venturi", "Venturini", "Vestri", "Vettori", "Vichi", "Viciani", "Vieri", "Vigiani", "Vignoli", "Vignolini", "Vignozzi", "Villani", "Vinci", "Visani", "Vitale", "Vitali", "Viti", "Viviani", "Vivoli", "Volpe", "Volpi", "Wang", "Wu", "Xu", "Yang", "Ye", "Zagli", "Zani", "Zanieri", "Zanobini", "Zecchi", "Zetti", "Zhang", "Zheng", "Zhou", "Zhu", "Zingoni", "Zini", "Zoppi"], // http://www.voornamelijk.nl/meest-voorkomende-achternamen-in-nederland-en-amsterdam/ "nl":["Albers", "Alblas", "Appelman", "Baars", "Baas", "Bakker", "Blank", "Bleeker", "Blok", "Blom", "Boer", "Boers", "Boldewijn", "Boon", "Boot", "Bos", "Bosch", "Bosma", "Bosman", "Bouma", "Bouman", "Bouwman", "Brands", "Brouwer", "Burger", "Buijs", "Buitenhuis", "Ceder", "Cohen", "Dekker", "Dekkers", "Dijkman", "Dijkstra", "Driessen", "Drost", "Engel", "Evers", "Faber", "Franke", "Gerritsen", "Goedhart", "Goossens", "Groen", "Groenenberg", "Groot", "Haan", "Hart", "Heemskerk", "Hendriks", "Hermans", "Hoekstra", "Hofman", "Hopman", "Huisman", "Jacobs", "Jansen", "Janssen", "Jonker", "Jaspers", "Keijzer", "Klaassen", "Klein", "Koek", "Koenders", "Kok", "Kool", "Koopman", "Koopmans", "Koning", "Koster", "Kramer", "Kroon", "Kuijpers", "Kuiper", "Kuipers", "Kurt", "Koster", "Kwakman", "Los", "Lubbers", "Maas", "Markus", "Martens", "Meijer", "Mol", "Molenaar", "Mulder", "Nieuwenhuis", "Peeters", "Peters", "Pengel", "Pieters", "Pool", "Post", "Postma", "Prins", "Pronk", "Reijnders", "Rietveld", "Roest", "Roos", "Sanders", "Schaap", "Scheffer", "Schenk", "Schilder", "Schipper", "Schmidt", "Scholten", "Schouten", "Schut", "Schutte", "Schuurman", "Simons", "Smeets", "Smit", "Smits", "Snel", "Swinkels", "Tas", "Terpstra", "Timmermans", "Tol", "Tromp", "Troost", "Valk", "Veenstra", "Veldkamp", "Verbeek", "Verheul", "Verhoeven", "Vermeer", "Vermeulen", "Verweij", "Vink", "Visser", "Voorn", "Vos", "Wagenaar", "Wiersema", "Willems", "Willemsen", "Witteveen", "Wolff", "Wolters", "Zijlstra", "Zwart", "de Beer", "de Boer", "de Bruijn", "de Bruin", "de Graaf", "de Groot", "de Haan", "de Haas", "de Jager", "de Jong", "de Jonge", "de Koning", "de Lange", "de Leeuw", "de Ridder", "de Rooij", "de Ruiter", "de Vos", "de Vries", "de Waal", "de Wit", "de Zwart", "van Beek", "van Boven", "van Dam", "van Dijk", "van Dongen", "van Doorn", "van Egmond", "van Eijk", "van Es", "van Gelder", "van Gelderen", "van Houten", "van Hulst", "van Kempen", "van Kesteren", "van Leeuwen", "van Loon", "van Mill", "van Noord", "van Ommen", "van Ommeren", "van Oosten", "van Oostveen", "van Rijn", "van Schaik", "van Veen", "van Vliet", "van Wijk", "van Wijngaarden", "van den Poel", "van de Pol", "van den Ploeg", "van de Ven", "van den Berg", "van den Bosch", "van den Brink", "van den Broek", "van den Heuvel", "van der Heijden", "van der Horst", "van der Hulst", "van der Kroon", "van der Laan", "van der Linden", "van der Meer", "van der Meij", "van der Meulen", "van der Molen", "van der Sluis", "van der Spek", "van der Veen", "van der Velde", "van der Velden", "van der Vliet", "van der Wal"] }, // Data taken from https://github.com/umpirsky/country-list/blob/master/data/en_US/country.json countries: [{"name":"Afghanistan","abbreviation":"AF"},{"name":"Åland Islands","abbreviation":"AX"},{"name":"Albania","abbreviation":"AL"},{"name":"Algeria","abbreviation":"DZ"},{"name":"American Samoa","abbreviation":"AS"},{"name":"Andorra","abbreviation":"AD"},{"name":"Angola","abbreviation":"AO"},{"name":"Anguilla","abbreviation":"AI"},{"name":"Antarctica","abbreviation":"AQ"},{"name":"Antigua & Barbuda","abbreviation":"AG"},{"name":"Argentina","abbreviation":"AR"},{"name":"Armenia","abbreviation":"AM"},{"name":"Aruba","abbreviation":"AW"},{"name":"Ascension Island","abbreviation":"AC"},{"name":"Australia","abbreviation":"AU"},{"name":"Austria","abbreviation":"AT"},{"name":"Azerbaijan","abbreviation":"AZ"},{"name":"Bahamas","abbreviation":"BS"},{"name":"Bahrain","abbreviation":"BH"},{"name":"Bangladesh","abbreviation":"BD"},{"name":"Barbados","abbreviation":"BB"},{"name":"Belarus","abbreviation":"BY"},{"name":"Belgium","abbreviation":"BE"},{"name":"Belize","abbreviation":"BZ"},{"name":"Benin","abbreviation":"BJ"},{"name":"Bermuda","abbreviation":"BM"},{"name":"Bhutan","abbreviation":"BT"},{"name":"Bolivia","abbreviation":"BO"},{"name":"Bosnia & Herzegovina","abbreviation":"BA"},{"name":"Botswana","abbreviation":"BW"},{"name":"Brazil","abbreviation":"BR"},{"name":"British Indian Ocean Territory","abbreviation":"IO"},{"name":"British Virgin Islands","abbreviation":"VG"},{"name":"Brunei","abbreviation":"BN"},{"name":"Bulgaria","abbreviation":"BG"},{"name":"Burkina Faso","abbreviation":"BF"},{"name":"Burundi","abbreviation":"BI"},{"name":"Cambodia","abbreviation":"KH"},{"name":"Cameroon","abbreviation":"CM"},{"name":"Canada","abbreviation":"CA"},{"name":"Canary Islands","abbreviation":"IC"},{"name":"Cape Verde","abbreviation":"CV"},{"name":"Caribbean Netherlands","abbreviation":"BQ"},{"name":"Cayman Islands","abbreviation":"KY"},{"name":"Central African Republic","abbreviation":"CF"},{"name":"Ceuta & Melilla","abbreviation":"EA"},{"name":"Chad","abbreviation":"TD"},{"name":"Chile","abbreviation":"CL"},{"name":"China","abbreviation":"CN"},{"name":"Christmas Island","abbreviation":"CX"},{"name":"Cocos (Keeling) Islands","abbreviation":"CC"},{"name":"Colombia","abbreviation":"CO"},{"name":"Comoros","abbreviation":"KM"},{"name":"Congo - Brazzaville","abbreviation":"CG"},{"name":"Congo - Kinshasa","abbreviation":"CD"},{"name":"Cook Islands","abbreviation":"CK"},{"name":"Costa Rica","abbreviation":"CR"},{"name":"Côte d'Ivoire","abbreviation":"CI"},{"name":"Croatia","abbreviation":"HR"},{"name":"Cuba","abbreviation":"CU"},{"name":"Curaçao","abbreviation":"CW"},{"name":"Cyprus","abbreviation":"CY"},{"name":"Czech Republic","abbreviation":"CZ"},{"name":"Denmark","abbreviation":"DK"},{"name":"Diego Garcia","abbreviation":"DG"},{"name":"Djibouti","abbreviation":"DJ"},{"name":"Dominica","abbreviation":"DM"},{"name":"Dominican Republic","abbreviation":"DO"},{"name":"Ecuador","abbreviation":"EC"},{"name":"Egypt","abbreviation":"EG"},{"name":"El Salvador","abbreviation":"SV"},{"name":"Equatorial Guinea","abbreviation":"GQ"},{"name":"Eritrea","abbreviation":"ER"},{"name":"Estonia","abbreviation":"EE"},{"name":"Ethiopia","abbreviation":"ET"},{"name":"Falkland Islands","abbreviation":"FK"},{"name":"Faroe Islands","abbreviation":"FO"},{"name":"Fiji","abbreviation":"FJ"},{"name":"Finland","abbreviation":"FI"},{"name":"France","abbreviation":"FR"},{"name":"French Guiana","abbreviation":"GF"},{"name":"French Polynesia","abbreviation":"PF"},{"name":"French Southern Territories","abbreviation":"TF"},{"name":"Gabon","abbreviation":"GA"},{"name":"Gambia","abbreviation":"GM"},{"name":"Georgia","abbreviation":"GE"},{"name":"Germany","abbreviation":"DE"},{"name":"Ghana","abbreviation":"GH"},{"name":"Gibraltar","abbreviation":"GI"},{"name":"Greece","abbreviation":"GR"},{"name":"Greenland","abbreviation":"GL"},{"name":"Grenada","abbreviation":"GD"},{"name":"Guadeloupe","abbreviation":"GP"},{"name":"Guam","abbreviation":"GU"},{"name":"Guatemala","abbreviation":"GT"},{"name":"Guernsey","abbreviation":"GG"},{"name":"Guinea","abbreviation":"GN"},{"name":"Guinea-Bissau","abbreviation":"GW"},{"name":"Guyana","abbreviation":"GY"},{"name":"Haiti","abbreviation":"HT"},{"name":"Honduras","abbreviation":"HN"},{"name":"Hong Kong SAR China","abbreviation":"HK"},{"name":"Hungary","abbreviation":"HU"},{"name":"Iceland","abbreviation":"IS"},{"name":"India","abbreviation":"IN"},{"name":"Indonesia","abbreviation":"ID"},{"name":"Iran","abbreviation":"IR"},{"name":"Iraq","abbreviation":"IQ"},{"name":"Ireland","abbreviation":"IE"},{"name":"Isle of Man","abbreviation":"IM"},{"name":"Israel","abbreviation":"IL"},{"name":"Italy","abbreviation":"IT"},{"name":"Jamaica","abbreviation":"JM"},{"name":"Japan","abbreviation":"JP"},{"name":"Jersey","abbreviation":"JE"},{"name":"Jordan","abbreviation":"JO"},{"name":"Kazakhstan","abbreviation":"KZ"},{"name":"Kenya","abbreviation":"KE"},{"name":"Kiribati","abbreviation":"KI"},{"name":"Kosovo","abbreviation":"XK"},{"name":"Kuwait","abbreviation":"KW"},{"name":"Kyrgyzstan","abbreviation":"KG"},{"name":"Laos","abbreviation":"LA"},{"name":"Latvia","abbreviation":"LV"},{"name":"Lebanon","abbreviation":"LB"},{"name":"Lesotho","abbreviation":"LS"},{"name":"Liberia","abbreviation":"LR"},{"name":"Libya","abbreviation":"LY"},{"name":"Liechtenstein","abbreviation":"LI"},{"name":"Lithuania","abbreviation":"LT"},{"name":"Luxembourg","abbreviation":"LU"},{"name":"Macau SAR China","abbreviation":"MO"},{"name":"Macedonia","abbreviation":"MK"},{"name":"Madagascar","abbreviation":"MG"},{"name":"Malawi","abbreviation":"MW"},{"name":"Malaysia","abbreviation":"MY"},{"name":"Maldives","abbreviation":"MV"},{"name":"Mali","abbreviation":"ML"},{"name":"Malta","abbreviation":"MT"},{"name":"Marshall Islands","abbreviation":"MH"},{"name":"Martinique","abbreviation":"MQ"},{"name":"Mauritania","abbreviation":"MR"},{"name":"Mauritius","abbreviation":"MU"},{"name":"Mayotte","abbreviation":"YT"},{"name":"Mexico","abbreviation":"MX"},{"name":"Micronesia","abbreviation":"FM"},{"name":"Moldova","abbreviation":"MD"},{"name":"Monaco","abbreviation":"MC"},{"name":"Mongolia","abbreviation":"MN"},{"name":"Montenegro","abbreviation":"ME"},{"name":"Montserrat","abbreviation":"MS"},{"name":"Morocco","abbreviation":"MA"},{"name":"Mozambique","abbreviation":"MZ"},{"name":"Myanmar (Burma)","abbreviation":"MM"},{"name":"Namibia","abbreviation":"NA"},{"name":"Nauru","abbreviation":"NR"},{"name":"Nepal","abbreviation":"NP"},{"name":"Netherlands","abbreviation":"NL"},{"name":"New Caledonia","abbreviation":"NC"},{"name":"New Zealand","abbreviation":"NZ"},{"name":"Nicaragua","abbreviation":"NI"},{"name":"Niger","abbreviation":"NE"},{"name":"Nigeria","abbreviation":"NG"},{"name":"Niue","abbreviation":"NU"},{"name":"Norfolk Island","abbreviation":"NF"},{"name":"North Korea","abbreviation":"KP"},{"name":"Northern Mariana Islands","abbreviation":"MP"},{"name":"Norway","abbreviation":"NO"},{"name":"Oman","abbreviation":"OM"},{"name":"Pakistan","abbreviation":"PK"},{"name":"Palau","abbreviation":"PW"},{"name":"Palestinian Territories","abbreviation":"PS"},{"name":"Panama","abbreviation":"PA"},{"name":"Papua New Guinea","abbreviation":"PG"},{"name":"Paraguay","abbreviation":"PY"},{"name":"Peru","abbreviation":"PE"},{"name":"Philippines","abbreviation":"PH"},{"name":"Pitcairn Islands","abbreviation":"PN"},{"name":"Poland","abbreviation":"PL"},{"name":"Portugal","abbreviation":"PT"},{"name":"Puerto Rico","abbreviation":"PR"},{"name":"Qatar","abbreviation":"QA"},{"name":"Réunion","abbreviation":"RE"},{"name":"Romania","abbreviation":"RO"},{"name":"Russia","abbreviation":"RU"},{"name":"Rwanda","abbreviation":"RW"},{"name":"Samoa","abbreviation":"WS"},{"name":"San Marino","abbreviation":"SM"},{"name":"São Tomé and Príncipe","abbreviation":"ST"},{"name":"Saudi Arabia","abbreviation":"SA"},{"name":"Senegal","abbreviation":"SN"},{"name":"Serbia","abbreviation":"RS"},{"name":"Seychelles","abbreviation":"SC"},{"name":"Sierra Leone","abbreviation":"SL"},{"name":"Singapore","abbreviation":"SG"},{"name":"Sint Maarten","abbreviation":"SX"},{"name":"Slovakia","abbreviation":"SK"},{"name":"Slovenia","abbreviation":"SI"},{"name":"Solomon Islands","abbreviation":"SB"},{"name":"Somalia","abbreviation":"SO"},{"name":"South Africa","abbreviation":"ZA"},{"name":"South Georgia & South Sandwich Islands","abbreviation":"GS"},{"name":"South Korea","abbreviation":"KR"},{"name":"South Sudan","abbreviation":"SS"},{"name":"Spain","abbreviation":"ES"},{"name":"Sri Lanka","abbreviation":"LK"},{"name":"St. Barthélemy","abbreviation":"BL"},{"name":"St. Helena","abbreviation":"SH"},{"name":"St. Kitts & Nevis","abbreviation":"KN"},{"name":"St. Lucia","abbreviation":"LC"},{"name":"St. Martin","abbreviation":"MF"},{"name":"St. Pierre & Miquelon","abbreviation":"PM"},{"name":"St. Vincent & Grenadines","abbreviation":"VC"},{"name":"Sudan","abbreviation":"SD"},{"name":"Suriname","abbreviation":"SR"},{"name":"Svalbard & Jan Mayen","abbreviation":"SJ"},{"name":"Swaziland","abbreviation":"SZ"},{"name":"Sweden","abbreviation":"SE"},{"name":"Switzerland","abbreviation":"CH"},{"name":"Syria","abbreviation":"SY"},{"name":"Taiwan","abbreviation":"TW"},{"name":"Tajikistan","abbreviation":"TJ"},{"name":"Tanzania","abbreviation":"TZ"},{"name":"Thailand","abbreviation":"TH"},{"name":"Timor-Leste","abbreviation":"TL"},{"name":"Togo","abbreviation":"TG"},{"name":"Tokelau","abbreviation":"TK"},{"name":"Tonga","abbreviation":"TO"},{"name":"Trinidad & Tobago","abbreviation":"TT"},{"name":"Tristan da Cunha","abbreviation":"TA"},{"name":"Tunisia","abbreviation":"TN"},{"name":"Turkey","abbreviation":"TR"},{"name":"Turkmenistan","abbreviation":"TM"},{"name":"Turks & Caicos Islands","abbreviation":"TC"},{"name":"Tuvalu","abbreviation":"TV"},{"name":"U.S. Outlying Islands","abbreviation":"UM"},{"name":"U.S. Virgin Islands","abbreviation":"VI"},{"name":"Uganda","abbreviation":"UG"},{"name":"Ukraine","abbreviation":"UA"},{"name":"United Arab Emirates","abbreviation":"AE"},{"name":"United Kingdom","abbreviation":"GB"},{"name":"United States","abbreviation":"US"},{"name":"Uruguay","abbreviation":"UY"},{"name":"Uzbekistan","abbreviation":"UZ"},{"name":"Vanuatu","abbreviation":"VU"},{"name":"Vatican City","abbreviation":"VA"},{"name":"Venezuela","abbreviation":"VE"},{"name":"Vietnam","abbreviation":"VN"},{"name":"Wallis & Futuna","abbreviation":"WF"},{"name":"Western Sahara","abbreviation":"EH"},{"name":"Yemen","abbreviation":"YE"},{"name":"Zambia","abbreviation":"ZM"},{"name":"Zimbabwe","abbreviation":"ZW"}], counties: { // Data taken from http://www.downloadexcelfiles.com/gb_en/download-excel-file-list-counties-uk "uk": [ {name: 'Bath and North East Somerset'}, {name: 'Aberdeenshire'}, {name: 'Anglesey'}, {name: 'Angus'}, {name: 'Bedford'}, {name: 'Blackburn with Darwen'}, {name: 'Blackpool'}, {name: 'Bournemouth'}, {name: 'Bracknell Forest'}, {name: 'Brighton & Hove'}, {name: 'Bristol'}, {name: 'Buckinghamshire'}, {name: 'Cambridgeshire'}, {name: 'Carmarthenshire'}, {name: 'Central Bedfordshire'}, {name: 'Ceredigion'}, {name: 'Cheshire East'}, {name: 'Cheshire West and Chester'}, {name: 'Clackmannanshire'}, {name: 'Conwy'}, {name: 'Cornwall'}, {name: 'County Antrim'}, {name: 'County Armagh'}, {name: 'County Down'}, {name: 'County Durham'}, {name: 'County Fermanagh'}, {name: 'County Londonderry'}, {name: 'County Tyrone'}, {name: 'Cumbria'}, {name: 'Darlington'}, {name: 'Denbighshire'}, {name: 'Derby'}, {name: 'Derbyshire'}, {name: 'Devon'}, {name: 'Dorset'}, {name: 'Dumfries and Galloway'}, {name: 'Dundee'}, {name: 'East Lothian'}, {name: 'East Riding of Yorkshire'}, {name: 'East Sussex'}, {name: 'Edinburgh?'}, {name: 'Essex'}, {name: 'Falkirk'}, {name: 'Fife'}, {name: 'Flintshire'}, {name: 'Gloucestershire'}, {name: 'Greater London'}, {name: 'Greater Manchester'}, {name: 'Gwent'}, {name: 'Gwynedd'}, {name: 'Halton'}, {name: 'Hampshire'}, {name: 'Hartlepool'}, {name: 'Herefordshire'}, {name: 'Hertfordshire'}, {name: 'Highlands'}, {name: 'Hull'}, {name: 'Isle of Wight'}, {name: 'Isles of Scilly'}, {name: 'Kent'}, {name: 'Lancashire'}, {name: 'Leicester'}, {name: 'Leicestershire'}, {name: 'Lincolnshire'}, {name: 'Lothian'}, {name: 'Luton'}, {name: 'Medway'}, {name: 'Merseyside'}, {name: 'Mid Glamorgan'}, {name: 'Middlesbrough'}, {name: 'Milton Keynes'}, {name: 'Monmouthshire'}, {name: 'Moray'}, {name: 'Norfolk'}, {name: 'North East Lincolnshire'}, {name: 'North Lincolnshire'}, {name: 'North Somerset'}, {name: 'North Yorkshire'}, {name: 'Northamptonshire'}, {name: 'Northumberland'}, {name: 'Nottingham'}, {name: 'Nottinghamshire'}, {name: 'Oxfordshire'}, {name: 'Pembrokeshire'}, {name: 'Perth and Kinross'}, {name: 'Peterborough'}, {name: 'Plymouth'}, {name: 'Poole'}, {name: 'Portsmouth'}, {name: 'Powys'}, {name: 'Reading'}, {name: 'Redcar and Cleveland'}, {name: 'Rutland'}, {name: 'Scottish Borders'}, {name: 'Shropshire'}, {name: 'Slough'}, {name: 'Somerset'}, {name: 'South Glamorgan'}, {name: 'South Gloucestershire'}, {name: 'South Yorkshire'}, {name: 'Southampton'}, {name: 'Southend-on-Sea'}, {name: 'Staffordshire'}, {name: 'Stirlingshire'}, {name: 'Stockton-on-Tees'}, {name: 'Stoke-on-Trent'}, {name: 'Strathclyde'}, {name: 'Suffolk'}, {name: 'Surrey'}, {name: 'Swindon'}, {name: 'Telford and Wrekin'}, {name: 'Thurrock'}, {name: 'Torbay'}, {name: 'Tyne and Wear'}, {name: 'Warrington'}, {name: 'Warwickshire'}, {name: 'West Berkshire'}, {name: 'West Glamorgan'}, {name: 'West Lothian'}, {name: 'West Midlands'}, {name: 'West Sussex'}, {name: 'West Yorkshire'}, {name: 'Western Isles'}, {name: 'Wiltshire'}, {name: 'Windsor and Maidenhead'}, {name: 'Wokingham'}, {name: 'Worcestershire'}, {name: 'Wrexham'}, {name: 'York'}] }, provinces: { "ca": [ {name: 'Alberta', abbreviation: 'AB'}, {name: 'British Columbia', abbreviation: 'BC'}, {name: 'Manitoba', abbreviation: 'MB'}, {name: 'New Brunswick', abbreviation: 'NB'}, {name: 'Newfoundland and Labrador', abbreviation: 'NL'}, {name: 'Nova Scotia', abbreviation: 'NS'}, {name: 'Ontario', abbreviation: 'ON'}, {name: 'Prince Edward Island', abbreviation: 'PE'}, {name: 'Quebec', abbreviation: 'QC'}, {name: 'Saskatchewan', abbreviation: 'SK'}, // The case could be made that the following are not actually provinces // since they are technically considered "territories" however they all // look the same on an envelope! {name: 'Northwest Territories', abbreviation: 'NT'}, {name: 'Nunavut', abbreviation: 'NU'}, {name: 'Yukon', abbreviation: 'YT'} ], "it": [ { name: "Agrigento", abbreviation: "AG", code: 84 }, { name: "Alessandria", abbreviation: "AL", code: 6 }, { name: "Ancona", abbreviation: "AN", code: 42 }, { name: "Aosta", abbreviation: "AO", code: 7 }, { name: "L'Aquila", abbreviation: "AQ", code: 66 }, { name: "Arezzo", abbreviation: "AR", code: 51 }, { name: "Ascoli-Piceno", abbreviation: "AP", code: 44 }, { name: "Asti", abbreviation: "AT", code: 5 }, { name: "Avellino", abbreviation: "AV", code: 64 }, { name: "Bari", abbreviation: "BA", code: 72 }, { name: "Barletta-Andria-Trani", abbreviation: "BT", code: 72 }, { name: "Belluno", abbreviation: "BL", code: 25 }, { name: "Benevento", abbreviation: "BN", code: 62 }, { name: "Bergamo", abbreviation: "BG", code: 16 }, { name: "Biella", abbreviation: "BI", code: 96 }, { name: "Bologna", abbreviation: "BO", code: 37 }, { name: "Bolzano", abbreviation: "BZ", code: 21 }, { name: "Brescia", abbreviation: "BS", code: 17 }, { name: "Brindisi", abbreviation: "BR", code: 74 }, { name: "Cagliari", abbreviation: "CA", code: 92 }, { name: "Caltanissetta", abbreviation: "CL", code: 85 }, { name: "Campobasso", abbreviation: "CB", code: 70 }, { name: "Carbonia Iglesias", abbreviation: "CI", code: 70 }, { name: "Caserta", abbreviation: "CE", code: 61 }, { name: "Catania", abbreviation: "CT", code: 87 }, { name: "Catanzaro", abbreviation: "CZ", code: 79 }, { name: "Chieti", abbreviation: "CH", code: 69 }, { name: "Como", abbreviation: "CO", code: 13 }, { name: "Cosenza", abbreviation: "CS", code: 78 }, { name: "Cremona", abbreviation: "CR", code: 19 }, { name: "Crotone", abbreviation: "KR", code: 101 }, { name: "Cuneo", abbreviation: "CN", code: 4 }, { name: "Enna", abbreviation: "EN", code: 86 }, { name: "Fermo", abbreviation: "FM", code: 86 }, { name: "Ferrara", abbreviation: "FE", code: 38 }, { name: "Firenze", abbreviation: "FI", code: 48 }, { name: "Foggia", abbreviation: "FG", code: 71 }, { name: "Forli-Cesena", abbreviation: "FC", code: 71 }, { name: "Frosinone", abbreviation: "FR", code: 60 }, { name: "Genova", abbreviation: "GE", code: 10 }, { name: "Gorizia", abbreviation: "GO", code: 31 }, { name: "Grosseto", abbreviation: "GR", code: 53 }, { name: "Imperia", abbreviation: "IM", code: 8 }, { name: "Isernia", abbreviation: "IS", code: 94 }, { name: "La-Spezia", abbreviation: "SP", code: 66 }, { name: "Latina", abbreviation: "LT", code: 59 }, { name: "Lecce", abbreviation: "LE", code: 75 }, { name: "Lecco", abbreviation: "LC", code: 97 }, { name: "Livorno", abbreviation: "LI", code: 49 }, { name: "Lodi", abbreviation: "LO", code: 98 }, { name: "Lucca", abbreviation: "LU", code: 46 }, { name: "Macerata", abbreviation: "MC", code: 43 }, { name: "Mantova", abbreviation: "MN", code: 20 }, { name: "Massa-Carrara", abbreviation: "MS", code: 45 }, { name: "Matera", abbreviation: "MT", code: 77 }, { name: "Medio Campidano", abbreviation: "VS", code: 77 }, { name: "Messina", abbreviation: "ME", code: 83 }, { name: "Milano", abbreviation: "MI", code: 15 }, { name: "Modena", abbreviation: "MO", code: 36 }, { name: "Monza-Brianza", abbreviation: "MB", code: 36 }, { name: "Napoli", abbreviation: "NA", code: 63 }, { name: "Novara", abbreviation: "NO", code: 3 }, { name: "Nuoro", abbreviation: "NU", code: 91 }, { name: "Ogliastra", abbreviation: "OG", code: 91 }, { name: "Olbia Tempio", abbreviation: "OT", code: 91 }, { name: "Oristano", abbreviation: "OR", code: 95 }, { name: "Padova", abbreviation: "PD", code: 28 }, { name: "Palermo", abbreviation: "PA", code: 82 }, { name: "Parma", abbreviation: "PR", code: 34 }, { name: "Pavia", abbreviation: "PV", code: 18 }, { name: "Perugia", abbreviation: "PG", code: 54 }, { name: "Pesaro-Urbino", abbreviation: "PU", code: 41 }, { name: "Pescara", abbreviation: "PE", code: 68 }, { name: "Piacenza", abbreviation: "PC", code: 33 }, { name: "Pisa", abbreviation: "PI", code: 50 }, { name: "Pistoia", abbreviation: "PT", code: 47 }, { name: "Pordenone", abbreviation: "PN", code: 93 }, { name: "Potenza", abbreviation: "PZ", code: 76 }, { name: "Prato", abbreviation: "PO", code: 100 }, { name: "Ragusa", abbreviation: "RG", code: 88 }, { name: "Ravenna", abbreviation: "RA", code: 39 }, { name: "Reggio-Calabria", abbreviation: "RC", code: 35 }, { name: "Reggio-Emilia", abbreviation: "RE", code: 35 }, { name: "Rieti", abbreviation: "RI", code: 57 }, { name: "Rimini", abbreviation: "RN", code: 99 }, { name: "Roma", abbreviation: "Roma", code: 58 }, { name: "Rovigo", abbreviation: "RO", code: 29 }, { name: "Salerno", abbreviation: "SA", code: 65 }, { name: "Sassari", abbreviation: "SS", code: 90 }, { name: "Savona", abbreviation: "SV", code: 9 }, { name: "Siena", abbreviation: "SI", code: 52 }, { name: "Siracusa", abbreviation: "SR", code: 89 }, { name: "Sondrio", abbreviation: "SO", code: 14 }, { name: "Taranto", abbreviation: "TA", code: 73 }, { name: "Teramo", abbreviation: "TE", code: 67 }, { name: "Terni", abbreviation: "TR", code: 55 }, { name: "Torino", abbreviation: "TO", code: 1 }, { name: "Trapani", abbreviation: "TP", code: 81 }, { name: "Trento", abbreviation: "TN", code: 22 }, { name: "Treviso", abbreviation: "TV", code: 26 }, { name: "Trieste", abbreviation: "TS", code: 32 }, { name: "Udine", abbreviation: "UD", code: 30 }, { name: "Varese", abbreviation: "VA", code: 12 }, { name: "Venezia", abbreviation: "VE", code: 27 }, { name: "Verbania", abbreviation: "VB", code: 27 }, { name: "Vercelli", abbreviation: "VC", code: 2 }, { name: "Verona", abbreviation: "VR", code: 23 }, { name: "Vibo-Valentia", abbreviation: "VV", code: 102 }, { name: "Vicenza", abbreviation: "VI", code: 24 }, { name: "Viterbo", abbreviation: "VT", code: 56 } ] }, // from: https://github.com/samsargent/Useful-Autocomplete-Data/blob/master/data/nationalities.json nationalities: [ {name: 'Afghan'}, {name: 'Albanian'}, {name: 'Algerian'}, {name: 'American'}, {name: 'Andorran'}, {name: 'Angolan'}, {name: 'Antiguans'}, {name: 'Argentinean'}, {name: 'Armenian'}, {name: 'Australian'}, {name: 'Austrian'}, {name: 'Azerbaijani'}, {name: 'Bahami'}, {name: 'Bahraini'}, {name: 'Bangladeshi'}, {name: 'Barbadian'}, {name: 'Barbudans'}, {name: 'Batswana'}, {name: 'Belarusian'}, {name: 'Belgian'}, {name: 'Belizean'}, {name: 'Beninese'}, {name: 'Bhutanese'}, {name: 'Bolivian'}, {name: 'Bosnian'}, {name: 'Brazilian'}, {name: 'British'}, {name: 'Bruneian'}, {name: 'Bulgarian'}, {name: 'Burkinabe'}, {name: 'Burmese'}, {name: 'Burundian'}, {name: 'Cambodian'}, {name: 'Cameroonian'}, {name: 'Canadian'}, {name: 'Cape Verdean'}, {name: 'Central African'}, {name: 'Chadian'}, {name: 'Chilean'}, {name: 'Chinese'}, {name: 'Colombian'}, {name: 'Comoran'}, {name: 'Congolese'}, {name: 'Costa Rican'}, {name: 'Croatian'}, {name: 'Cuban'}, {name: 'Cypriot'}, {name: 'Czech'}, {name: 'Danish'}, {name: 'Djibouti'}, {name: 'Dominican'}, {name: 'Dutch'}, {name: 'East Timorese'}, {name: 'Ecuadorean'}, {name: 'Egyptian'}, {name: 'Emirian'}, {name: 'Equatorial Guinean'}, {name: 'Eritrean'}, {name: 'Estonian'}, {name: 'Ethiopian'}, {name: 'Fijian'}, {name: 'Filipino'}, {name: 'Finnish'}, {name: 'French'}, {name: 'Gabonese'}, {name: 'Gambian'}, {name: 'Georgian'}, {name: 'German'}, {name: 'Ghanaian'}, {name: 'Greek'}, {name: 'Grenadian'}, {name: 'Guatemalan'}, {name: 'Guinea-Bissauan'}, {name: 'Guinean'}, {name: 'Guyanese'}, {name: 'Haitian'}, {name: 'Herzegovinian'}, {name: 'Honduran'}, {name: 'Hungarian'}, {name: 'I-Kiribati'}, {name: 'Icelander'}, {name: 'Indian'}, {name: 'Indonesian'}, {name: 'Iranian'}, {name: 'Iraqi'}, {name: 'Irish'}, {name: 'Israeli'}, {name: 'Italian'}, {name: 'Ivorian'}, {name: 'Jamaican'}, {name: 'Japanese'}, {name: 'Jordanian'}, {name: 'Kazakhstani'}, {name: 'Kenyan'}, {name: 'Kittian and Nevisian'}, {name: 'Kuwaiti'}, {name: 'Kyrgyz'}, {name: 'Laotian'}, {name: 'Latvian'}, {name: 'Lebanese'}, {name: 'Liberian'}, {name: 'Libyan'}, {name: 'Liechtensteiner'}, {name: 'Lithuanian'}, {name: 'Luxembourger'}, {name: 'Macedonian'}, {name: 'Malagasy'}, {name: 'Malawian'}, {name: 'Malaysian'}, {name: 'Maldivan'}, {name: 'Malian'}, {name: 'Maltese'}, {name: 'Marshallese'}, {name: 'Mauritanian'}, {name: 'Mauritian'}, {name: 'Mexican'}, {name: 'Micronesian'}, {name: 'Moldovan'}, {name: 'Monacan'}, {name: 'Mongolian'}, {name: 'Moroccan'}, {name: 'Mosotho'}, {name: 'Motswana'}, {name: 'Mozambican'}, {name: 'Namibian'}, {name: 'Nauruan'}, {name: 'Nepalese'}, {name: 'New Zealander'}, {name: 'Nicaraguan'}, {name: 'Nigerian'}, {name: 'Nigerien'}, {name: 'North Korean'}, {name: 'Northern Irish'}, {name: 'Norwegian'}, {name: 'Omani'}, {name: 'Pakistani'}, {name: 'Palauan'}, {name: 'Panamanian'}, {name: 'Papua New Guinean'}, {name: 'Paraguayan'}, {name: 'Peruvian'}, {name: 'Polish'}, {name: 'Portuguese'}, {name: 'Qatari'}, {name: 'Romani'}, {name: 'Russian'}, {name: 'Rwandan'}, {name: 'Saint Lucian'}, {name: 'Salvadoran'}, {name: 'Samoan'}, {name: 'San Marinese'}, {name: 'Sao Tomean'}, {name: 'Saudi'}, {name: 'Scottish'}, {name: 'Senegalese'}, {name: 'Serbian'}, {name: 'Seychellois'}, {name: 'Sierra Leonean'}, {name: 'Singaporean'}, {name: 'Slovakian'}, {name: 'Slovenian'}, {name: 'Solomon Islander'}, {name: 'Somali'}, {name: 'South African'}, {name: 'South Korean'}, {name: 'Spanish'}, {name: 'Sri Lankan'}, {name: 'Sudanese'}, {name: 'Surinamer'}, {name: 'Swazi'}, {name: 'Swedish'}, {name: 'Swiss'}, {name: 'Syrian'}, {name: 'Taiwanese'}, {name: 'Tajik'}, {name: 'Tanzanian'}, {name: 'Thai'}, {name: 'Togolese'}, {name: 'Tongan'}, {name: 'Trinidadian or Tobagonian'}, {name: 'Tunisian'}, {name: 'Turkish'}, {name: 'Tuvaluan'}, {name: 'Ugandan'}, {name: 'Ukrainian'}, {name: 'Uruguaya'}, {name: 'Uzbekistani'}, {name: 'Venezuela'}, {name: 'Vietnamese'}, {name: 'Wels'}, {name: 'Yemenit'}, {name: 'Zambia'}, {name: 'Zimbabwe'}, ], // http://www.loc.gov/standards/iso639-2/php/code_list.php (ISO-639-1 codes) locale_languages: [ "aa", "ab", "ae", "af", "ak", "am", "an", "ar", "as", "av", "ay", "az", "ba", "be", "bg", "bh", "bi", "bm", "bn", "bo", "br", "bs", "ca", "ce", "ch", "co", "cr", "cs", "cu", "cv", "cy", "da", "de", "dv", "dz", "ee", "el", "en", "eo", "es", "et", "eu", "fa", "ff", "fi", "fj", "fo", "fr", "fy", "ga", "gd", "gl", "gn", "gu", "gv", "ha", "he", "hi", "ho", "hr", "ht", "hu", "hy", "hz", "ia", "id", "ie", "ig", "ii", "ik", "io", "is", "it", "iu", "ja", "jv", "ka", "kg", "ki", "kj", "kk", "kl", "km", "kn", "ko", "kr", "ks", "ku", "kv", "kw", "ky", "la", "lb", "lg", "li", "ln", "lo", "lt", "lu", "lv", "mg", "mh", "mi", "mk", "ml", "mn", "mr", "ms", "mt", "my", "na", "nb", "nd", "ne", "ng", "nl", "nn", "no", "nr", "nv", "ny", "oc", "oj", "om", "or", "os", "pa", "pi", "pl", "ps", "pt", "qu", "rm", "rn", "ro", "ru", "rw", "sa", "sc", "sd", "se", "sg", "si", "sk", "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw", "ty", "ug", "uk", "ur", "uz", "ve", "vi", "vo", "wa", "wo", "xh", "yi", "yo", "za", "zh", "zu" ], // From http://data.okfn.org/data/core/language-codes#resource-language-codes-full (IETF language tags) locale_regions: [ "agq-CM", "asa-TZ", "ast-ES", "bas-CM", "bem-ZM", "bez-TZ", "brx-IN", "cgg-UG", "chr-US", "dav-KE", "dje-NE", "dsb-DE", "dua-CM", "dyo-SN", "ebu-KE", "ewo-CM", "fil-PH", "fur-IT", "gsw-CH", "gsw-FR", "gsw-LI", "guz-KE", "haw-US", "hsb-DE", "jgo-CM", "jmc-TZ", "kab-DZ", "kam-KE", "kde-TZ", "kea-CV", "khq-ML", "kkj-CM", "kln-KE", "kok-IN", "ksb-TZ", "ksf-CM", "ksh-DE", "lag-TZ", "lkt-US", "luo-KE", "luy-KE", "mas-KE", "mas-TZ", "mer-KE", "mfe-MU", "mgh-MZ", "mgo-CM", "mua-CM", "naq-NA", "nmg-CM", "nnh-CM", "nus-SD", "nyn-UG", "rof-TZ", "rwk-TZ", "sah-RU", "saq-KE", "sbp-TZ", "seh-MZ", "ses-ML", "shi-Latn", "shi-Latn-MA", "shi-Tfng", "shi-Tfng-MA", "smn-FI", "teo-KE", "teo-UG", "twq-NE", "tzm-Latn", "tzm-Latn-MA", "vai-Latn", "vai-Latn-LR", "vai-Vaii", "vai-Vaii-LR", "vun-TZ", "wae-CH", "xog-UG", "yav-CM", "zgh-MA", "af-NA", "af-ZA", "ak-GH", "am-ET", "ar-001", "ar-AE", "ar-BH", "ar-DJ", "ar-DZ", "ar-EG", "ar-EH", "ar-ER", "ar-IL", "ar-IQ", "ar-JO", "ar-KM", "ar-KW", "ar-LB", "ar-LY", "ar-MA", "ar-MR", "ar-OM", "ar-PS", "ar-QA", "ar-SA", "ar-SD", "ar-SO", "ar-SS", "ar-SY", "ar-TD", "ar-TN", "ar-YE", "as-IN", "az-Cyrl", "az-Cyrl-AZ", "az-Latn", "az-Latn-AZ", "be-BY", "bg-BG", "bm-Latn", "bm-Latn-ML", "bn-BD", "bn-IN", "bo-CN", "bo-IN", "br-FR", "bs-Cyrl", "bs-Cyrl-BA", "bs-Latn", "bs-Latn-BA", "ca-AD", "ca-ES", "ca-ES-VALENCIA", "ca-FR", "ca-IT", "cs-CZ", "cy-GB", "da-DK", "da-GL", "de-AT", "de-BE", "de-CH", "de-DE", "de-LI", "de-LU", "dz-BT", "ee-GH", "ee-TG", "el-CY", "el-GR", "en-001", "en-150", "en-AG", "en-AI", "en-AS", "en-AU", "en-BB", "en-BE", "en-BM", "en-BS", "en-BW", "en-BZ", "en-CA", "en-CC", "en-CK", "en-CM", "en-CX", "en-DG", "en-DM", "en-ER", "en-FJ", "en-FK", "en-FM", "en-GB", "en-GD", "en-GG", "en-GH", "en-GI", "en-GM", "en-GU", "en-GY", "en-HK", "en-IE", "en-IM", "en-IN", "en-IO", "en-JE", "en-JM", "en-KE", "en-KI", "en-KN", "en-KY", "en-LC", "en-LR", "en-LS", "en-MG", "en-MH", "en-MO", "en-MP", "en-MS", "en-MT", "en-MU", "en-MW", "en-MY", "en-NA", "en-NF", "en-NG", "en-NR", "en-NU", "en-NZ", "en-PG", "en-PH", "en-PK", "en-PN", "en-PR", "en-PW", "en-RW", "en-SB", "en-SC", "en-SD", "en-SG", "en-SH", "en-SL", "en-SS", "en-SX", "en-SZ", "en-TC", "en-TK", "en-TO", "en-TT", "en-TV", "en-TZ", "en-UG", "en-UM", "en-US", "en-US-POSIX", "en-VC", "en-VG", "en-VI", "en-VU", "en-WS", "en-ZA", "en-ZM", "en-ZW", "eo-001", "es-419", "es-AR", "es-BO", "es-CL", "es-CO", "es-CR", "es-CU", "es-DO", "es-EA", "es-EC", "es-ES", "es-GQ", "es-GT", "es-HN", "es-IC", "es-MX", "es-NI", "es-PA", "es-PE", "es-PH", "es-PR", "es-PY", "es-SV", "es-US", "es-UY", "es-VE", "et-EE", "eu-ES", "fa-AF", "fa-IR", "ff-CM", "ff-GN", "ff-MR", "ff-SN", "fi-FI", "fo-FO", "fr-BE", "fr-BF", "fr-BI", "fr-BJ", "fr-BL", "fr-CA", "fr-CD", "fr-CF", "fr-CG", "fr-CH", "fr-CI", "fr-CM", "fr-DJ", "fr-DZ", "fr-FR", "fr-GA", "fr-GF", "fr-GN", "fr-GP", "fr-GQ", "fr-HT", "fr-KM", "fr-LU", "fr-MA", "fr-MC", "fr-MF", "fr-MG", "fr-ML", "fr-MQ", "fr-MR", "fr-MU", "fr-NC", "fr-NE", "fr-PF", "fr-PM", "fr-RE", "fr-RW", "fr-SC", "fr-SN", "fr-SY", "fr-TD", "fr-TG", "fr-TN", "fr-VU", "fr-WF", "fr-YT", "fy-NL", "ga-IE", "gd-GB", "gl-ES", "gu-IN", "gv-IM", "ha-Latn", "ha-Latn-GH", "ha-Latn-NE", "ha-Latn-NG", "he-IL", "hi-IN", "hr-BA", "hr-HR", "hu-HU", "hy-AM", "id-ID", "ig-NG", "ii-CN", "is-IS", "it-CH", "it-IT", "it-SM", "ja-JP", "ka-GE", "ki-KE", "kk-Cyrl", "kk-Cyrl-KZ", "kl-GL", "km-KH", "kn-IN", "ko-KP", "ko-KR", "ks-Arab", "ks-Arab-IN", "kw-GB", "ky-Cyrl", "ky-Cyrl-KG", "lb-LU", "lg-UG", "ln-AO", "ln-CD", "ln-CF", "ln-CG", "lo-LA", "lt-LT", "lu-CD", "lv-LV", "mg-MG", "mk-MK", "ml-IN", "mn-Cyrl", "mn-Cyrl-MN", "mr-IN", "ms-Latn", "ms-Latn-BN", "ms-Latn-MY", "ms-Latn-SG", "mt-MT", "my-MM", "nb-NO", "nb-SJ", "nd-ZW", "ne-IN", "ne-NP", "nl-AW", "nl-BE", "nl-BQ", "nl-CW", "nl-NL", "nl-SR", "nl-SX", "nn-NO", "om-ET", "om-KE", "or-IN", "os-GE", "os-RU", "pa-Arab", "pa-Arab-PK", "pa-Guru", "pa-Guru-IN", "pl-PL", "ps-AF", "pt-AO", "pt-BR", "pt-CV", "pt-GW", "pt-MO", "pt-MZ", "pt-PT", "pt-ST", "pt-TL", "qu-BO", "qu-EC", "qu-PE", "rm-CH", "rn-BI", "ro-MD", "ro-RO", "ru-BY", "ru-KG", "ru-KZ", "ru-MD", "ru-RU", "ru-UA", "rw-RW", "se-FI", "se-NO", "se-SE", "sg-CF", "si-LK", "sk-SK", "sl-SI", "sn-ZW", "so-DJ", "so-ET", "so-KE", "so-SO", "sq-AL", "sq-MK", "sq-XK", "sr-Cyrl", "sr-Cyrl-BA", "sr-Cyrl-ME", "sr-Cyrl-RS", "sr-Cyrl-XK", "sr-Latn", "sr-Latn-BA", "sr-Latn-ME", "sr-Latn-RS", "sr-Latn-XK", "sv-AX", "sv-FI", "sv-SE", "sw-CD", "sw-KE", "sw-TZ", "sw-UG", "ta-IN", "ta-LK", "ta-MY", "ta-SG", "te-IN", "th-TH", "ti-ER", "ti-ET", "to-TO", "tr-CY", "tr-TR", "ug-Arab", "ug-Arab-CN", "uk-UA", "ur-IN", "ur-PK", "uz-Arab", "uz-Arab-AF", "uz-Cyrl", "uz-Cyrl-UZ", "uz-Latn", "uz-Latn-UZ", "vi-VN", "yi-001", "yo-BJ", "yo-NG", "zh-Hans", "zh-Hans-CN", "zh-Hans-HK", "zh-Hans-MO", "zh-Hans-SG", "zh-Hant", "zh-Hant-HK", "zh-Hant-MO", "zh-Hant-TW", "zu-ZA" ], us_states_and_dc: [ {name: 'Alabama', abbreviation: 'AL'}, {name: 'Alaska', abbreviation: 'AK'}, {name: 'Arizona', abbreviation: 'AZ'}, {name: 'Arkansas', abbreviation: 'AR'}, {name: 'California', abbreviation: 'CA'}, {name: 'Colorado', abbreviation: 'CO'}, {name: 'Connecticut', abbreviation: 'CT'}, {name: 'Delaware', abbreviation: 'DE'}, {name: 'District of Columbia', abbreviation: 'DC'}, {name: 'Florida', abbreviation: 'FL'}, {name: 'Georgia', abbreviation: 'GA'}, {name: 'Hawaii', abbreviation: 'HI'}, {name: 'Idaho', abbreviation: 'ID'}, {name: 'Illinois', abbreviation: 'IL'}, {name: 'Indiana', abbreviation: 'IN'}, {name: 'Iowa', abbreviation: 'IA'}, {name: 'Kansas', abbreviation: 'KS'}, {name: 'Kentucky', abbreviation: 'KY'}, {name: 'Louisiana', abbreviation: 'LA'}, {name: 'Maine', abbreviation: 'ME'}, {name: 'Maryland', abbreviation: 'MD'}, {name: 'Massachusetts', abbreviation: 'MA'}, {name: 'Michigan', abbreviation: 'MI'}, {name: 'Minnesota', abbreviation: 'MN'}, {name: 'Mississippi', abbreviation: 'MS'}, {name: 'Missouri', abbreviation: 'MO'}, {name: 'Montana', abbreviation: 'MT'}, {name: 'Nebraska', abbreviation: 'NE'}, {name: 'Nevada', abbreviation: 'NV'}, {name: 'New Hampshire', abbreviation: 'NH'}, {name: 'New Jersey', abbreviation: 'NJ'}, {name: 'New Mexico', abbreviation: 'NM'}, {name: 'New York', abbreviation: 'NY'}, {name: 'North Carolina', abbreviation: 'NC'}, {name: 'North Dakota', abbreviation: 'ND'}, {name: 'Ohio', abbreviation: 'OH'}, {name: 'Oklahoma', abbreviation: 'OK'}, {name: 'Oregon', abbreviation: 'OR'}, {name: 'Pennsylvania', abbreviation: 'PA'}, {name: 'Rhode Island', abbreviation: 'RI'}, {name: 'South Carolina', abbreviation: 'SC'}, {name: 'South Dakota', abbreviation: 'SD'}, {name: 'Tennessee', abbreviation: 'TN'}, {name: 'Texas', abbreviation: 'TX'}, {name: 'Utah', abbreviation: 'UT'}, {name: 'Vermont', abbreviation: 'VT'}, {name: 'Virginia', abbreviation: 'VA'}, {name: 'Washington', abbreviation: 'WA'}, {name: 'West Virginia', abbreviation: 'WV'}, {name: 'Wisconsin', abbreviation: 'WI'}, {name: 'Wyoming', abbreviation: 'WY'} ], territories: [ {name: 'American Samoa', abbreviation: 'AS'}, {name: 'Federated States of Micronesia', abbreviation: 'FM'}, {name: 'Guam', abbreviation: 'GU'}, {name: 'Marshall Islands', abbreviation: 'MH'}, {name: 'Northern Mariana Islands', abbreviation: 'MP'}, {name: 'Puerto Rico', abbreviation: 'PR'}, {name: 'Virgin Islands, U.S.', abbreviation: 'VI'} ], armed_forces: [ {name: 'Armed Forces Europe', abbreviation: 'AE'}, {name: 'Armed Forces Pacific', abbreviation: 'AP'}, {name: 'Armed Forces the Americas', abbreviation: 'AA'} ], country_regions: { it: [ { name: "Valle d'Aosta", abbreviation: "VDA" }, { name: "Piemonte", abbreviation: "PIE" }, { name: "Lombardia", abbreviation: "LOM" }, { name: "Veneto", abbreviation: "VEN" }, { name: "Trentino Alto Adige", abbreviation: "TAA" }, { name: "Friuli Venezia Giulia", abbreviation: "FVG" }, { name: "Liguria", abbreviation: "LIG" }, { name: "Emilia Romagna", abbreviation: "EMR" }, { name: "Toscana", abbreviation: "TOS" }, { name: "Umbria", abbreviation: "UMB" }, { name: "Marche", abbreviation: "MAR" }, { name: "Abruzzo", abbreviation: "ABR" }, { name: "Lazio", abbreviation: "LAZ" }, { name: "Campania", abbreviation: "CAM" }, { name: "Puglia", abbreviation: "PUG" }, { name: "Basilicata", abbreviation: "BAS" }, { name: "Molise", abbreviation: "MOL" }, { name: "Calabria", abbreviation: "CAL" }, { name: "Sicilia", abbreviation: "SIC" }, { name: "Sardegna", abbreviation: "SAR" } ] }, street_suffixes: { 'us': [ {name: 'Avenue', abbreviation: 'Ave'}, {name: 'Boulevard', abbreviation: 'Blvd'}, {name: 'Center', abbreviation: 'Ctr'}, {name: 'Circle', abbreviation: 'Cir'}, {name: 'Court', abbreviation: 'Ct'}, {name: 'Drive', abbreviation: 'Dr'}, {name: 'Extension', abbreviation: 'Ext'}, {name: 'Glen', abbreviation: 'Gln'}, {name: 'Grove', abbreviation: 'Grv'}, {name: 'Heights', abbreviation: 'Hts'}, {name: 'Highway', abbreviation: 'Hwy'}, {name: 'Junction', abbreviation: 'Jct'}, {name: 'Key', abbreviation: 'Key'}, {name: 'Lane', abbreviation: 'Ln'}, {name: 'Loop', abbreviation: 'Loop'}, {name: 'Manor', abbreviation: 'Mnr'}, {name: 'Mill', abbreviation: 'Mill'}, {name: 'Park', abbreviation: 'Park'}, {name: 'Parkway', abbreviation: 'Pkwy'}, {name: 'Pass', abbreviation: 'Pass'}, {name: 'Path', abbreviation: 'Path'}, {name: 'Pike', abbreviation: 'Pike'}, {name: 'Place', abbreviation: 'Pl'}, {name: 'Plaza', abbreviation: 'Plz'}, {name: 'Point', abbreviation: 'Pt'}, {name: 'Ridge', abbreviation: 'Rdg'}, {name: 'River', abbreviation: 'Riv'}, {name: 'Road', abbreviation: 'Rd'}, {name: 'Square', abbreviation: 'Sq'}, {name: 'Street', abbreviation: 'St'}, {name: 'Terrace', abbreviation: 'Ter'}, {name: 'Trail', abbreviation: 'Trl'}, {name: 'Turnpike', abbreviation: 'Tpke'}, {name: 'View', abbreviation: 'Vw'}, {name: 'Way', abbreviation: 'Way'} ], 'it': [ { name: 'Accesso', abbreviation: 'Acc.' }, { name: 'Alzaia', abbreviation: 'Alz.' }, { name: 'Arco', abbreviation: 'Arco' }, { name: 'Archivolto', abbreviation: 'Acv.' }, { name: 'Arena', abbreviation: 'Arena' }, { name: 'Argine', abbreviation: 'Argine' }, { name: 'Bacino', abbreviation: 'Bacino' }, { name: 'Banchi', abbreviation: 'Banchi' }, { name: 'Banchina', abbreviation: 'Ban.' }, { name: 'Bastioni', abbreviation: 'Bas.' }, { name: 'Belvedere', abbreviation: 'Belv.' }, { name: 'Borgata', abbreviation: 'B.ta' }, { name: 'Borgo', abbreviation: 'B.go' }, { name: 'Calata', abbreviation: 'Cal.' }, { name: 'Calle', abbreviation: 'Calle' }, { name: 'Campiello', abbreviation: 'Cam.' }, { name: 'Campo', abbreviation: 'Cam.' }, { name: 'Canale', abbreviation: 'Can.' }, { name: 'Carraia', abbreviation: 'Carr.' }, { name: 'Cascina', abbreviation: 'Cascina' }, { name: 'Case sparse', abbreviation: 'c.s.' }, { name: 'Cavalcavia', abbreviation: 'Cv.' }, { name: 'Circonvallazione', abbreviation: 'Cv.' }, { name: 'Complanare', abbreviation: 'C.re' }, { name: 'Contrada', abbreviation: 'C.da' }, { name: 'Corso', abbreviation: 'C.so' }, { name: 'Corte', abbreviation: 'C.te' }, { name: 'Cortile', abbreviation: 'C.le' }, { name: 'Diramazione', abbreviation: 'Dir.' }, { name: 'Fondaco', abbreviation: 'F.co' }, { name: 'Fondamenta', abbreviation: 'F.ta' }, { name: 'Fondo', abbreviation: 'F.do' }, { name: 'Frazione', abbreviation: 'Fr.' }, { name: 'Isola', abbreviation: 'Is.' }, { name: 'Largo', abbreviation: 'L.go' }, { name: 'Litoranea', abbreviation: 'Lit.' }, { name: 'Lungolago', abbreviation: 'L.go lago' }, { name: 'Lungo Po', abbreviation: 'l.go Po' }, { name: 'Molo', abbreviation: 'Molo' }, { name: 'Mura', abbreviation: 'Mura' }, { name: 'Passaggio privato', abbreviation: 'pass. priv.' }, { name: 'Passeggiata', abbreviation: 'Pass.' }, { name: 'Piazza', abbreviation: 'P.zza' }, { name: 'Piazzale', abbreviation: 'P.le' }, { name: 'Ponte', abbreviation: 'P.te' }, { name: 'Portico', abbreviation: 'P.co' }, { name: 'Rampa', abbreviation: 'Rampa' }, { name: 'Regione', abbreviation: 'Reg.' }, { name: 'Rione', abbreviation: 'R.ne' }, { name: 'Rio', abbreviation: 'Rio' }, { name: 'Ripa', abbreviation: 'Ripa' }, { name: 'Riva', abbreviation: 'Riva' }, { name: 'Rondò', abbreviation: 'Rondò' }, { name: 'Rotonda', abbreviation: 'Rot.' }, { name: 'Sagrato', abbreviation: 'Sagr.' }, { name: 'Salita', abbreviation: 'Sal.' }, { name: 'Scalinata', abbreviation: 'Scal.' }, { name: 'Scalone', abbreviation: 'Scal.' }, { name: 'Slargo', abbreviation: 'Sl.' }, { name: 'Sottoportico', abbreviation: 'Sott.' }, { name: 'Strada', abbreviation: 'Str.' }, { name: 'Stradale', abbreviation: 'Str.le' }, { name: 'Strettoia', abbreviation: 'Strett.' }, { name: 'Traversa', abbreviation: 'Trav.' }, { name: 'Via', abbreviation: 'V.' }, { name: 'Viale', abbreviation: 'V.le' }, { name: 'Vicinale', abbreviation: 'Vic.le' }, { name: 'Vicolo', abbreviation: 'Vic.' } ], 'uk' : [ {name: 'Avenue', abbreviation: 'Ave'}, {name: 'Close', abbreviation: 'Cl'}, {name: 'Court', abbreviation: 'Ct'}, {name: 'Crescent', abbreviation: 'Cr'}, {name: 'Drive', abbreviation: 'Dr'}, {name: 'Garden', abbreviation: 'Gdn'}, {name: 'Gardens', abbreviation: 'Gdns'}, {name: 'Green', abbreviation: 'Gn'}, {name: 'Grove', abbreviation: 'Gr'}, {name: 'Lane', abbreviation: 'Ln'}, {name: 'Mount', abbreviation: 'Mt'}, {name: 'Place', abbreviation: 'Pl'}, {name: 'Park', abbreviation: 'Pk'}, {name: 'Ridge', abbreviation: 'Rdg'}, {name: 'Road', abbreviation: 'Rd'}, {name: 'Square', abbreviation: 'Sq'}, {name: 'Street', abbreviation: 'St'}, {name: 'Terrace', abbreviation: 'Ter'}, {name: 'Valley', abbreviation: 'Val'} ] }, months: [ {name: 'January', short_name: 'Jan', numeric: '01', days: 31}, // Not messing with leap years... {name: 'February', short_name: 'Feb', numeric: '02', days: 28}, {name: 'March', short_name: 'Mar', numeric: '03', days: 31}, {name: 'April', short_name: 'Apr', numeric: '04', days: 30}, {name: 'May', short_name: 'May', numeric: '05', days: 31}, {name: 'June', short_name: 'Jun', numeric: '06', days: 30}, {name: 'July', short_name: 'Jul', numeric: '07', days: 31}, {name: 'August', short_name: 'Aug', numeric: '08', days: 31}, {name: 'September', short_name: 'Sep', numeric: '09', days: 30}, {name: 'October', short_name: 'Oct', numeric: '10', days: 31}, {name: 'November', short_name: 'Nov', numeric: '11', days: 30}, {name: 'December', short_name: 'Dec', numeric: '12', days: 31} ], // http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29 cc_types: [ {name: "American Express", short_name: 'amex', prefix: '34', length: 15}, {name: "Bankcard", short_name: 'bankcard', prefix: '5610', length: 16}, {name: "China UnionPay", short_name: 'chinaunion', prefix: '62', length: 16}, {name: "Diners Club Carte Blanche", short_name: 'dccarte', prefix: '300', length: 14}, {name: "Diners Club enRoute", short_name: 'dcenroute', prefix: '2014', length: 15}, {name: "Diners Club International", short_name: 'dcintl', prefix: '36', length: 14}, {name: "Diners Club United States & Canada", short_name: 'dcusc', prefix: '54', length: 16}, {name: "Discover Card", short_name: 'discover', prefix: '6011', length: 16}, {name: "InstaPayment", short_name: 'instapay', prefix: '637', length: 16}, {name: "JCB", short_name: 'jcb', prefix: '3528', length: 16}, {name: "Laser", short_name: 'laser', prefix: '6304', length: 16}, {name: "Maestro", short_name: 'maestro', prefix: '5018', length: 16}, {name: "Mastercard", short_name: 'mc', prefix: '51', length: 16}, {name: "Solo", short_name: 'solo', prefix: '6334', length: 16}, {name: "Switch", short_name: 'switch', prefix: '4903', length: 16}, {name: "Visa", short_name: 'visa', prefix: '4', length: 16}, {name: "Visa Electron", short_name: 'electron', prefix: '4026', length: 16} ], //return all world currency by ISO 4217 currency_types: [ {'code' : 'AED', 'name' : 'United Arab Emirates Dirham'}, {'code' : 'AFN', 'name' : 'Afghanistan Afghani'}, {'code' : 'ALL', 'name' : 'Albania Lek'}, {'code' : 'AMD', 'name' : 'Armenia Dram'}, {'code' : 'ANG', 'name' : 'Netherlands Antilles Guilder'}, {'code' : 'AOA', 'name' : 'Angola Kwanza'}, {'code' : 'ARS', 'name' : 'Argentina Peso'}, {'code' : 'AUD', 'name' : 'Australia Dollar'}, {'code' : 'AWG', 'name' : 'Aruba Guilder'}, {'code' : 'AZN', 'name' : 'Azerbaijan New Manat'}, {'code' : 'BAM', 'name' : 'Bosnia and Herzegovina Convertible Marka'}, {'code' : 'BBD', 'name' : 'Barbados Dollar'}, {'code' : 'BDT', 'name' : 'Bangladesh Taka'}, {'code' : 'BGN', 'name' : 'Bulgaria Lev'}, {'code' : 'BHD', 'name' : 'Bahrain Dinar'}, {'code' : 'BIF', 'name' : 'Burundi Franc'}, {'code' : 'BMD', 'name' : 'Bermuda Dollar'}, {'code' : 'BND', 'name' : 'Brunei Darussalam Dollar'}, {'code' : 'BOB', 'name' : 'Bolivia Boliviano'}, {'code' : 'BRL', 'name' : 'Brazil Real'}, {'code' : 'BSD', 'name' : 'Bahamas Dollar'}, {'code' : 'BTN', 'name' : 'Bhutan Ngultrum'}, {'code' : 'BWP', 'name' : 'Botswana Pula'}, {'code' : 'BYR', 'name' : 'Belarus Ruble'}, {'code' : 'BZD', 'name' : 'Belize Dollar'}, {'code' : 'CAD', 'name' : 'Canada Dollar'}, {'code' : 'CDF', 'name' : 'Congo/Kinshasa Franc'}, {'code' : 'CHF', 'name' : 'Switzerland Franc'}, {'code' : 'CLP', 'name' : 'Chile Peso'}, {'code' : 'CNY', 'name' : 'China Yuan Renminbi'}, {'code' : 'COP', 'name' : 'Colombia Peso'}, {'code' : 'CRC', 'name' : 'Costa Rica Colon'}, {'code' : 'CUC', 'name' : 'Cuba Convertible Peso'}, {'code' : 'CUP', 'name' : 'Cuba Peso'}, {'code' : 'CVE', 'name' : 'Cape Verde Escudo'}, {'code' : 'CZK', 'name' : 'Czech Republic Koruna'}, {'code' : 'DJF', 'name' : 'Djibouti Franc'}, {'code' : 'DKK', 'name' : 'Denmark Krone'}, {'code' : 'DOP', 'name' : 'Dominican Republic Peso'}, {'code' : 'DZD', 'name' : 'Algeria Dinar'}, {'code' : 'EGP', 'name' : 'Egypt Pound'}, {'code' : 'ERN', 'name' : 'Eritrea Nakfa'}, {'code' : 'ETB', 'name' : 'Ethiopia Birr'}, {'code' : 'EUR', 'name' : 'Euro Member Countries'}, {'code' : 'FJD', 'name' : 'Fiji Dollar'}, {'code' : 'FKP', 'name' : 'Falkland Islands (Malvinas) Pound'}, {'code' : 'GBP', 'name' : 'United Kingdom Pound'}, {'code' : 'GEL', 'name' : 'Georgia Lari'}, {'code' : 'GGP', 'name' : 'Guernsey Pound'}, {'code' : 'GHS', 'name' : 'Ghana Cedi'}, {'code' : 'GIP', 'name' : 'Gibraltar Pound'}, {'code' : 'GMD', 'name' : 'Gambia Dalasi'}, {'code' : 'GNF', 'name' : 'Guinea Franc'}, {'code' : 'GTQ', 'name' : 'Guatemala Quetzal'}, {'code' : 'GYD', 'name' : 'Guyana Dollar'}, {'code' : 'HKD', 'name' : 'Hong Kong Dollar'}, {'code' : 'HNL', 'name' : 'Honduras Lempira'}, {'code' : 'HRK', 'name' : 'Croatia Kuna'}, {'code' : 'HTG', 'name' : 'Haiti Gourde'}, {'code' : 'HUF', 'name' : 'Hungary Forint'}, {'code' : 'IDR', 'name' : 'Indonesia Rupiah'}, {'code' : 'ILS', 'name' : 'Israel Shekel'}, {'code' : 'IMP', 'name' : 'Isle of Man Pound'}, {'code' : 'INR', 'name' : 'India Rupee'}, {'code' : 'IQD', 'name' : 'Iraq Dinar'}, {'code' : 'IRR', 'name' : 'Iran Rial'}, {'code' : 'ISK', 'name' : 'Iceland Krona'}, {'code' : 'JEP', 'name' : 'Jersey Pound'}, {'code' : 'JMD', 'name' : 'Jamaica Dollar'}, {'code' : 'JOD', 'name' : 'Jordan Dinar'}, {'code' : 'JPY', 'name' : 'Japan Yen'}, {'code' : 'KES', 'name' : 'Kenya Shilling'}, {'code' : 'KGS', 'name' : 'Kyrgyzstan Som'}, {'code' : 'KHR', 'name' : 'Cambodia Riel'}, {'code' : 'KMF', 'name' : 'Comoros Franc'}, {'code' : 'KPW', 'name' : 'Korea (North) Won'}, {'code' : 'KRW', 'name' : 'Korea (South) Won'}, {'code' : 'KWD', 'name' : 'Kuwait Dinar'}, {'code' : 'KYD', 'name' : 'Cayman Islands Dollar'}, {'code' : 'KZT', 'name' : 'Kazakhstan Tenge'}, {'code' : 'LAK', 'name' : 'Laos Kip'}, {'code' : 'LBP', 'name' : 'Lebanon Pound'}, {'code' : 'LKR', 'name' : 'Sri Lanka Rupee'}, {'code' : 'LRD', 'name' : 'Liberia Dollar'}, {'code' : 'LSL', 'name' : 'Lesotho Loti'}, {'code' : 'LTL', 'name' : 'Lithuania Litas'}, {'code' : 'LYD', 'name' : 'Libya Dinar'}, {'code' : 'MAD', 'name' : 'Morocco Dirham'}, {'code' : 'MDL', 'name' : 'Moldova Leu'}, {'code' : 'MGA', 'name' : 'Madagascar Ariary'}, {'code' : 'MKD', 'name' : 'Macedonia Denar'}, {'code' : 'MMK', 'name' : 'Myanmar (Burma) Kyat'}, {'code' : 'MNT', 'name' : 'Mongolia Tughrik'}, {'code' : 'MOP', 'name' : 'Macau Pataca'}, {'code' : 'MRO', 'name' : 'Mauritania Ouguiya'}, {'code' : 'MUR', 'name' : 'Mauritius Rupee'}, {'code' : 'MVR', 'name' : 'Maldives (Maldive Islands) Rufiyaa'}, {'code' : 'MWK', 'name' : 'Malawi Kwacha'}, {'code' : 'MXN', 'name' : 'Mexico Peso'}, {'code' : 'MYR', 'name' : 'Malaysia Ringgit'}, {'code' : 'MZN', 'name' : 'Mozambique Metical'}, {'code' : 'NAD', 'name' : 'Namibia Dollar'}, {'code' : 'NGN', 'name' : 'Nigeria Naira'}, {'code' : 'NIO', 'name' : 'Nicaragua Cordoba'}, {'code' : 'NOK', 'name' : 'Norway Krone'}, {'code' : 'NPR', 'name' : 'Nepal Rupee'}, {'code' : 'NZD', 'name' : 'New Zealand Dollar'}, {'code' : 'OMR', 'name' : 'Oman Rial'}, {'code' : 'PAB', 'name' : 'Panama Balboa'}, {'code' : 'PEN', 'name' : 'Peru Nuevo Sol'}, {'code' : 'PGK', 'name' : 'Papua New Guinea Kina'}, {'code' : 'PHP', 'name' : 'Philippines Peso'}, {'code' : 'PKR', 'name' : 'Pakistan Rupee'}, {'code' : 'PLN', 'name' : 'Poland Zloty'}, {'code' : 'PYG', 'name' : 'Paraguay Guarani'}, {'code' : 'QAR', 'name' : 'Qatar Riyal'}, {'code' : 'RON', 'name' : 'Romania New Leu'}, {'code' : 'RSD', 'name' : 'Serbia Dinar'}, {'code' : 'RUB', 'name' : 'Russia Ruble'}, {'code' : 'RWF', 'name' : 'Rwanda Franc'}, {'code' : 'SAR', 'name' : 'Saudi Arabia Riyal'}, {'code' : 'SBD', 'name' : 'Solomon Islands Dollar'}, {'code' : 'SCR', 'name' : 'Seychelles Rupee'}, {'code' : 'SDG', 'name' : 'Sudan Pound'}, {'code' : 'SEK', 'name' : 'Sweden Krona'}, {'code' : 'SGD', 'name' : 'Singapore Dollar'}, {'code' : 'SHP', 'name' : 'Saint Helena Pound'}, {'code' : 'SLL', 'name' : 'Sierra Leone Leone'}, {'code' : 'SOS', 'name' : 'Somalia Shilling'}, {'code' : 'SPL', 'name' : 'Seborga Luigino'}, {'code' : 'SRD', 'name' : 'Suriname Dollar'}, {'code' : 'STD', 'name' : 'São Tomé and Príncipe Dobra'}, {'code' : 'SVC', 'name' : 'El Salvador Colon'}, {'code' : 'SYP', 'name' : 'Syria Pound'}, {'code' : 'SZL', 'name' : 'Swaziland Lilangeni'}, {'code' : 'THB', 'name' : 'Thailand Baht'}, {'code' : 'TJS', 'name' : 'Tajikistan Somoni'}, {'code' : 'TMT', 'name' : 'Turkmenistan Manat'}, {'code' : 'TND', 'name' : 'Tunisia Dinar'}, {'code' : 'TOP', 'name' : 'Tonga Pa\'anga'}, {'code' : 'TRY', 'name' : 'Turkey Lira'}, {'code' : 'TTD', 'name' : 'Trinidad and Tobago Dollar'}, {'code' : 'TVD', 'name' : 'Tuvalu Dollar'}, {'code' : 'TWD', 'name' : 'Taiwan New Dollar'}, {'code' : 'TZS', 'name' : 'Tanzania Shilling'}, {'code' : 'UAH', 'name' : 'Ukraine Hryvnia'}, {'code' : 'UGX', 'name' : 'Uganda Shilling'}, {'code' : 'USD', 'name' : 'United States Dollar'}, {'code' : 'UYU', 'name' : 'Uruguay Peso'}, {'code' : 'UZS', 'name' : 'Uzbekistan Som'}, {'code' : 'VEF', 'name' : 'Venezuela Bolivar'}, {'code' : 'VND', 'name' : 'Viet Nam Dong'}, {'code' : 'VUV', 'name' : 'Vanuatu Vatu'}, {'code' : 'WST', 'name' : 'Samoa Tala'}, {'code' : 'XAF', 'name' : 'Communauté Financière Africaine (BEAC) CFA Franc BEAC'}, {'code' : 'XCD', 'name' : 'East Caribbean Dollar'}, {'code' : 'XDR', 'name' : 'International Monetary Fund (IMF) Special Drawing Rights'}, {'code' : 'XOF', 'name' : 'Communauté Financière Africaine (BCEAO) Franc'}, {'code' : 'XPF', 'name' : 'Comptoirs Français du Pacifique (CFP) Franc'}, {'code' : 'YER', 'name' : 'Yemen Rial'}, {'code' : 'ZAR', 'name' : 'South Africa Rand'}, {'code' : 'ZMW', 'name' : 'Zambia Kwacha'}, {'code' : 'ZWD', 'name' : 'Zimbabwe Dollar'} ], // return the names of all valide colors colorNames : [ "AliceBlue", "Black", "Navy", "DarkBlue", "MediumBlue", "Blue", "DarkGreen", "Green", "Teal", "DarkCyan", "DeepSkyBlue", "DarkTurquoise", "MediumSpringGreen", "Lime", "SpringGreen", "Aqua", "Cyan", "MidnightBlue", "DodgerBlue", "LightSeaGreen", "ForestGreen", "SeaGreen", "DarkSlateGray", "LimeGreen", "MediumSeaGreen", "Turquoise", "RoyalBlue", "SteelBlue", "DarkSlateBlue", "MediumTurquoise", "Indigo", "DarkOliveGreen", "CadetBlue", "CornflowerBlue", "RebeccaPurple", "MediumAquaMarine", "DimGray", "SlateBlue", "OliveDrab", "SlateGray", "LightSlateGray", "MediumSlateBlue", "LawnGreen", "Chartreuse", "Aquamarine", "Maroon", "Purple", "Olive", "Gray", "SkyBlue", "LightSkyBlue", "BlueViolet", "DarkRed", "DarkMagenta", "SaddleBrown", "Ivory", "White", "DarkSeaGreen", "LightGreen", "MediumPurple", "DarkViolet", "PaleGreen", "DarkOrchid", "YellowGreen", "Sienna", "Brown", "DarkGray", "LightBlue", "GreenYellow", "PaleTurquoise", "LightSteelBlue", "PowderBlue", "FireBrick", "DarkGoldenRod", "MediumOrchid", "RosyBrown", "DarkKhaki", "Silver", "MediumVioletRed", "IndianRed", "Peru", "Chocolate", "Tan", "LightGray", "Thistle", "Orchid", "GoldenRod", "PaleVioletRed", "Crimson", "Gainsboro", "Plum", "BurlyWood", "LightCyan", "Lavender", "DarkSalmon", "Violet", "PaleGoldenRod", "LightCoral", "Khaki", "AliceBlue", "HoneyDew", "Azure", "SandyBrown", "Wheat", "Beige", "WhiteSmoke", "MintCream", "GhostWhite", "Salmon", "AntiqueWhite", "Linen", "LightGoldenRodYellow", "OldLace", "Red", "Fuchsia", "Magenta", "DeepPink", "OrangeRed", "Tomato", "HotPink", "Coral", "DarkOrange", "LightSalmon", "Orange", "LightPink", "Pink", "Gold", "PeachPuff", "NavajoWhite", "Moccasin", "Bisque", "MistyRose", "BlanchedAlmond", "PapayaWhip", "LavenderBlush", "SeaShell", "Cornsilk", "LemonChiffon", "FloralWhite", "Snow", "Yellow", "LightYellow" ], // Data taken from https://www.sec.gov/rules/other/4-460list.htm company: [ "3Com Corp", "3M Company", "A.G. Edwards Inc.", "Abbott Laboratories", "Abercrombie & Fitch Co.", "ABM Industries Incorporated", "Ace Hardware Corporation", "ACT Manufacturing Inc.", "Acterna Corp.", "Adams Resources & Energy, Inc.", "ADC Telecommunications, Inc.", "Adelphia Communications Corporation", "Administaff, Inc.", "Adobe Systems Incorporated", "Adolph Coors Company", "Advance Auto Parts, Inc.", "Advanced Micro Devices, Inc.", "AdvancePCS, Inc.", "Advantica Restaurant Group, Inc.", "The AES Corporation", "Aetna Inc.", "Affiliated Computer Services, Inc.", "AFLAC Incorporated", "AGCO Corporation", "Agilent Technologies, Inc.", "Agway Inc.", "Apartment Investment and Management Company", "Air Products and Chemicals, Inc.", "Airborne, Inc.", "Airgas, Inc.", "AK Steel Holding Corporation", "Alaska Air Group, Inc.", "Alberto-Culver Company", "Albertson's, Inc.", "Alcoa Inc.", "Alleghany Corporation", "Allegheny Energy, Inc.", "Allegheny Technologies Incorporated", "Allergan, Inc.", "ALLETE, Inc.", "Alliant Energy Corporation", "Allied Waste Industries, Inc.", "Allmerica Financial Corporation", "The Allstate Corporation", "ALLTEL Corporation", "The Alpine Group, Inc.", "Amazon.com, Inc.", "AMC Entertainment Inc.", "American Power Conversion Corporation", "Amerada Hess Corporation", "AMERCO", "Ameren Corporation", "America West Holdings Corporation", "American Axle & Manufacturing Holdings, Inc.", "American Eagle Outfitters, Inc.", "American Electric Power Company, Inc.", "American Express Company", "American Financial Group, Inc.", "American Greetings Corporation", "American International Group, Inc.", "American Standard Companies Inc.", "American Water Works Company, Inc.", "AmerisourceBergen Corporation", "Ames Department Stores, Inc.", "Amgen Inc.", "Amkor Technology, Inc.", "AMR Corporation", "AmSouth Bancorp.", "Amtran, Inc.", "Anadarko Petroleum Corporation", "Analog Devices, Inc.", "Anheuser-Busch Companies, Inc.", "Anixter International Inc.", "AnnTaylor Inc.", "Anthem, Inc.", "AOL Time Warner Inc.", "Aon Corporation", "Apache Corporation", "Apple Computer, Inc.", "Applera Corporation", "Applied Industrial Technologies, Inc.", "Applied Materials, Inc.", "Aquila, Inc.", "ARAMARK Corporation", "Arch Coal, Inc.", "Archer Daniels Midland Company", "Arkansas Best Corporation", "Armstrong Holdings, Inc.", "Arrow Electronics, Inc.", "ArvinMeritor, Inc.", "Ashland Inc.", "Astoria Financial Corporation", "AT&T Corp.", "Atmel Corporation", "Atmos Energy Corporation", "Audiovox Corporation", "Autoliv, Inc.", "Automatic Data Processing, Inc.", "AutoNation, Inc.", "AutoZone, Inc.", "Avaya Inc.", "Avery Dennison Corporation", "Avista Corporation", "Avnet, Inc.", "Avon Products, Inc.", "Baker Hughes Incorporated", "Ball Corporation", "Bank of America Corporation", "The Bank of New York Company, Inc.", "Bank One Corporation", "Banknorth Group, Inc.", "Banta Corporation", "Barnes & Noble, Inc.", "Bausch & Lomb Incorporated", "Baxter International Inc.", "BB&T Corporation", "The Bear Stearns Companies Inc.", "Beazer Homes USA, Inc.", "Beckman Coulter, Inc.", "Becton, Dickinson and Company", "Bed Bath & Beyond Inc.", "Belk, Inc.", "Bell Microproducts Inc.", "BellSouth Corporation", "Belo Corp.", "Bemis Company, Inc.", "Benchmark Electronics, Inc.", "Berkshire Hathaway Inc.", "Best Buy Co., Inc.", "Bethlehem Steel Corporation", "Beverly Enterprises, Inc.", "Big Lots, Inc.", "BJ Services Company", "BJ's Wholesale Club, Inc.", "The Black & Decker Corporation", "Black Hills Corporation", "BMC Software, Inc.", "The Boeing Company", "Boise Cascade Corporation", "Borders Group, Inc.", "BorgWarner Inc.", "Boston Scientific Corporation", "Bowater Incorporated", "Briggs & Stratton Corporation", "Brightpoint, Inc.", "Brinker International, Inc.", "Bristol-Myers Squibb Company", "Broadwing, Inc.", "Brown Shoe Company, Inc.", "Brown-Forman Corporation", "Brunswick Corporation", "Budget Group, Inc.", "Burlington Coat Factory Warehouse Corporation", "Burlington Industries, Inc.", "Burlington Northern Santa Fe Corporation", "Burlington Resources Inc.", "C. H. Robinson Worldwide Inc.", "Cablevision Systems Corp", "Cabot Corp", "Cadence Design Systems, Inc.", "Calpine Corp.", "Campbell Soup Co.", "Capital One Financial Corp.", "Cardinal Health Inc.", "Caremark Rx Inc.", "Carlisle Cos. Inc.", "Carpenter Technology Corp.", "Casey's General Stores Inc.", "Caterpillar Inc.", "CBRL Group Inc.", "CDI Corp.", "CDW Computer Centers Inc.", "CellStar Corp.", "Cendant Corp", "Cenex Harvest States Cooperatives", "Centex Corp.", "CenturyTel Inc.", "Ceridian Corp.", "CH2M Hill Cos. Ltd.", "Champion Enterprises Inc.", "Charles Schwab Corp.", "Charming Shoppes Inc.", "Charter Communications Inc.", "Charter One Financial Inc.", "ChevronTexaco Corp.", "Chiquita Brands International Inc.", "Chubb Corp", "Ciena Corp.", "Cigna Corp", "Cincinnati Financial Corp.", "Cinergy Corp.", "Cintas Corp.", "Circuit City Stores Inc.", "Cisco Systems Inc.", "Citigroup, Inc", "Citizens Communications Co.", "CKE Restaurants Inc.", "Clear Channel Communications Inc.", "The Clorox Co.", "CMGI Inc.", "CMS Energy Corp.", "CNF Inc.", "Coca-Cola Co.", "Coca-Cola Enterprises Inc.", "Colgate-Palmolive Co.", "Collins & Aikman Corp.", "Comcast Corp.", "Comdisco Inc.", "Comerica Inc.", "Comfort Systems USA Inc.", "Commercial Metals Co.", "Community Health Systems Inc.", "Compass Bancshares Inc", "Computer Associates International Inc.", "Computer Sciences Corp.", "Compuware Corp.", "Comverse Technology Inc.", "ConAgra Foods Inc.", "Concord EFS Inc.", "Conectiv, Inc", "Conoco Inc", "Conseco Inc.", "Consolidated Freightways Corp.", "Consolidated Edison Inc.", "Constellation Brands Inc.", "Constellation Emergy Group Inc.", "Continental Airlines Inc.", "Convergys Corp.", "Cooper Cameron Corp.", "Cooper Industries Ltd.", "Cooper Tire & Rubber Co.", "Corn Products International Inc.", "Corning Inc.", "Costco Wholesale Corp.", "Countrywide Credit Industries Inc.", "Coventry Health Care Inc.", "Cox Communications Inc.", "Crane Co.", "Crompton Corp.", "Crown Cork & Seal Co. Inc.", "CSK Auto Corp.", "CSX Corp.", "Cummins Inc.", "CVS Corp.", "Cytec Industries Inc.", "D&K Healthcare Resources, Inc.", "D.R. Horton Inc.", "Dana Corporation", "Danaher Corporation", "Darden Restaurants Inc.", "DaVita Inc.", "Dean Foods Company", "Deere & Company", "Del Monte Foods Co", "Dell Computer Corporation", "Delphi Corp.", "Delta Air Lines Inc.", "Deluxe Corporation", "Devon Energy Corporation", "Di Giorgio Corporation", "Dial Corporation", "Diebold Incorporated", "Dillard's Inc.", "DIMON Incorporated", "Dole Food Company, Inc.", "Dollar General Corporation", "Dollar Tree Stores, Inc.", "Dominion Resources, Inc.", "Domino's Pizza LLC", "Dover Corporation, Inc.", "Dow Chemical Company", "Dow Jones & Company, Inc.", "DPL Inc.", "DQE Inc.", "Dreyer's Grand Ice Cream, Inc.", "DST Systems, Inc.", "DTE Energy Co.", "E.I. Du Pont de Nemours and Company", "Duke Energy Corp", "Dun & Bradstreet Inc.", "DURA Automotive Systems Inc.", "DynCorp", "Dynegy Inc.", "E*Trade Group, Inc.", "E.W. Scripps Company", "Earthlink, Inc.", "Eastman Chemical Company", "Eastman Kodak Company", "Eaton Corporation", "Echostar Communications Corporation", "Ecolab Inc.", "Edison International", "EGL Inc.", "El Paso Corporation", "Electronic Arts Inc.", "Electronic Data Systems Corp.", "Eli Lilly and Company", "EMC Corporation", "Emcor Group Inc.", "Emerson Electric Co.", "Encompass Services Corporation", "Energizer Holdings Inc.", "Energy East Corporation", "Engelhard Corporation", "Enron Corp.", "Entergy Corporation", "Enterprise Products Partners L.P.", "EOG Resources, Inc.", "Equifax Inc.", "Equitable Resources Inc.", "Equity Office Properties Trust", "Equity Residential Properties Trust", "Estee Lauder Companies Inc.", "Exelon Corporation", "Exide Technologies", "Expeditors International of Washington Inc.", "Express Scripts Inc.", "ExxonMobil Corporation", "Fairchild Semiconductor International Inc.", "Family Dollar Stores Inc.", "Farmland Industries Inc.", "Federal Mogul Corp.", "Federated Department Stores Inc.", "Federal Express Corp.", "Felcor Lodging Trust Inc.", "Ferro Corp.", "Fidelity National Financial Inc.", "Fifth Third Bancorp", "First American Financial Corp.", "First Data Corp.", "First National of Nebraska Inc.", "First Tennessee National Corp.", "FirstEnergy Corp.", "Fiserv Inc.", "Fisher Scientific International Inc.", "FleetBoston Financial Co.", "Fleetwood Enterprises Inc.", "Fleming Companies Inc.", "Flowers Foods Inc.", "Flowserv Corp", "Fluor Corp", "FMC Corp", "Foamex International Inc", "Foot Locker Inc", "Footstar Inc.", "Ford Motor Co", "Forest Laboratories Inc.", "Fortune Brands Inc.", "Foster Wheeler Ltd.", "FPL Group Inc.", "Franklin Resources Inc.", "Freeport McMoran Copper & Gold Inc.", "Frontier Oil Corp", "Furniture Brands International Inc.", "Gannett Co., Inc.", "Gap Inc.", "Gateway Inc.", "GATX Corporation", "Gemstar-TV Guide International Inc.", "GenCorp Inc.", "General Cable Corporation", "General Dynamics Corporation", "General Electric Company", "General Mills Inc", "General Motors Corporation", "Genesis Health Ventures Inc.", "Gentek Inc.", "Gentiva Health Services Inc.", "Genuine Parts Company", "Genuity Inc.", "Genzyme Corporation", "Georgia Gulf Corporation", "Georgia-Pacific Corporation", "Gillette Company", "Gold Kist Inc.", "Golden State Bancorp Inc.", "Golden West Financial Corporation", "Goldman Sachs Group Inc.", "Goodrich Corporation", "The Goodyear Tire & Rubber Company", "Granite Construction Incorporated", "Graybar Electric Company Inc.", "Great Lakes Chemical Corporation", "Great Plains Energy Inc.", "GreenPoint Financial Corp.", "Greif Bros. Corporation", "Grey Global Group Inc.", "Group 1 Automotive Inc.", "Guidant Corporation", "H&R Block Inc.", "H.B. Fuller Company", "H.J. Heinz Company", "Halliburton Co.", "Harley-Davidson Inc.", "Harman International Industries Inc.", "Harrah's Entertainment Inc.", "Harris Corp.", "Harsco Corp.", "Hartford Financial Services Group Inc.", "Hasbro Inc.", "Hawaiian Electric Industries Inc.", "HCA Inc.", "Health Management Associates Inc.", "Health Net Inc.", "Healthsouth Corp", "Henry Schein Inc.", "Hercules Inc.", "Herman Miller Inc.", "Hershey Foods Corp.", "Hewlett-Packard Company", "Hibernia Corp.", "Hillenbrand Industries Inc.", "Hilton Hotels Corp.", "Hollywood Entertainment Corp.", "Home Depot Inc.", "Hon Industries Inc.", "Honeywell International Inc.", "Hormel Foods Corp.", "Host Marriott Corp.", "Household International Corp.", "Hovnanian Enterprises Inc.", "Hub Group Inc.", "Hubbell Inc.", "Hughes Supply Inc.", "Humana Inc.", "Huntington Bancshares Inc.", "Idacorp Inc.", "IDT Corporation", "IKON Office Solutions Inc.", "Illinois Tool Works Inc.", "IMC Global Inc.", "Imperial Sugar Company", "IMS Health Inc.", "Ingles Market Inc", "Ingram Micro Inc.", "Insight Enterprises Inc.", "Integrated Electrical Services Inc.", "Intel Corporation", "International Paper Co.", "Interpublic Group of Companies Inc.", "Interstate Bakeries Corporation", "International Business Machines Corp.", "International Flavors & Fragrances Inc.", "International Multifoods Corporation", "Intuit Inc.", "IT Group Inc.", "ITT Industries Inc.", "Ivax Corp.", "J.B. Hunt Transport Services Inc.", "J.C. Penny Co.", "J.P. Morgan Chase & Co.", "Jabil Circuit Inc.", "Jack In The Box Inc.", "Jacobs Engineering Group Inc.", "JDS Uniphase Corp.", "Jefferson-Pilot Co.", "John Hancock Financial Services Inc.", "Johnson & Johnson", "Johnson Controls Inc.", "Jones Apparel Group Inc.", "KB Home", "Kellogg Company", "Kellwood Company", "Kelly Services Inc.", "Kemet Corp.", "Kennametal Inc.", "Kerr-McGee Corporation", "KeyCorp", "KeySpan Corp.", "Kimball International Inc.", "Kimberly-Clark Corporation", "Kindred Healthcare Inc.", "KLA-Tencor Corporation", "K-Mart Corp.", "Knight-Ridder Inc.", "Kohl's Corp.", "KPMG Consulting Inc.", "Kroger Co.", "L-3 Communications Holdings Inc.", "Laboratory Corporation of America Holdings", "Lam Research Corporation", "LandAmerica Financial Group Inc.", "Lands' End Inc.", "Landstar System Inc.", "La-Z-Boy Inc.", "Lear Corporation", "Legg Mason Inc.", "Leggett & Platt Inc.", "Lehman Brothers Holdings Inc.", "Lennar Corporation", "Lennox International Inc.", "Level 3 Communications Inc.", "Levi Strauss & Co.", "Lexmark International Inc.", "Limited Inc.", "Lincoln National Corporation", "Linens 'n Things Inc.", "Lithia Motors Inc.", "Liz Claiborne Inc.", "Lockheed Martin Corporation", "Loews Corporation", "Longs Drug Stores Corporation", "Louisiana-Pacific Corporation", "Lowe's Companies Inc.", "LSI Logic Corporation", "The LTV Corporation", "The Lubrizol Corporation", "Lucent Technologies Inc.", "Lyondell Chemical Company", "M & T Bank Corporation", "Magellan Health Services Inc.", "Mail-Well Inc.", "Mandalay Resort Group", "Manor Care Inc.", "Manpower Inc.", "Marathon Oil Corporation", "Mariner Health Care Inc.", "Markel Corporation", "Marriott International Inc.", "Marsh & McLennan Companies Inc.", "Marsh Supermarkets Inc.", "Marshall & Ilsley Corporation", "Martin Marietta Materials Inc.", "Masco Corporation", "Massey Energy Company", "MasTec Inc.", "Mattel Inc.", "Maxim Integrated Products Inc.", "Maxtor Corporation", "Maxxam Inc.", "The May Department Stores Company", "Maytag Corporation", "MBNA Corporation", "McCormick & Company Incorporated", "McDonald's Corporation", "The McGraw-Hill Companies Inc.", "McKesson Corporation", "McLeodUSA Incorporated", "M.D.C. Holdings Inc.", "MDU Resources Group Inc.", "MeadWestvaco Corporation", "Medtronic Inc.", "Mellon Financial Corporation", "The Men's Wearhouse Inc.", "Merck & Co., Inc.", "Mercury General Corporation", "Merrill Lynch & Co. Inc.", "Metaldyne Corporation", "Metals USA Inc.", "MetLife Inc.", "Metris Companies Inc", "MGIC Investment Corporation", "MGM Mirage", "Michaels Stores Inc.", "Micron Technology Inc.", "Microsoft Corporation", "Milacron Inc.", "Millennium Chemicals Inc.", "Mirant Corporation", "Mohawk Industries Inc.", "Molex Incorporated", "The MONY Group Inc.", "Morgan Stanley Dean Witter & Co.", "Motorola Inc.", "MPS Group Inc.", "Murphy Oil Corporation", "Nabors Industries Inc", "Nacco Industries Inc", "Nash Finch Company", "National City Corp.", "National Commerce Financial Corporation", "National Fuel Gas Company", "National Oilwell Inc", "National Rural Utilities Cooperative Finance Corporation", "National Semiconductor Corporation", "National Service Industries Inc", "Navistar International Corporation", "NCR Corporation", "The Neiman Marcus Group Inc.", "New Jersey Resources Corporation", "New York Times Company", "Newell Rubbermaid Inc", "Newmont Mining Corporation", "Nextel Communications Inc", "Nicor Inc", "Nike Inc", "NiSource Inc", "Noble Energy Inc", "Nordstrom Inc", "Norfolk Southern Corporation", "Nortek Inc", "North Fork Bancorporation Inc", "Northeast Utilities System", "Northern Trust Corporation", "Northrop Grumman Corporation", "NorthWestern Corporation", "Novellus Systems Inc", "NSTAR", "NTL Incorporated", "Nucor Corp", "Nvidia Corp", "NVR Inc", "Northwest Airlines Corp", "Occidental Petroleum Corp", "Ocean Energy Inc", "Office Depot Inc.", "OfficeMax Inc", "OGE Energy Corp", "Oglethorpe Power Corp.", "Ohio Casualty Corp.", "Old Republic International Corp.", "Olin Corp.", "OM Group Inc", "Omnicare Inc", "Omnicom Group", "On Semiconductor Corp", "ONEOK Inc", "Oracle Corp", "Oshkosh Truck Corp", "Outback Steakhouse Inc.", "Owens & Minor Inc.", "Owens Corning", "Owens-Illinois Inc", "Oxford Health Plans Inc", "Paccar Inc", "PacifiCare Health Systems Inc", "Packaging Corp. of America", "Pactiv Corp", "Pall Corp", "Pantry Inc", "Park Place Entertainment Corp", "Parker Hannifin Corp.", "Pathmark Stores Inc.", "Paychex Inc", "Payless Shoesource Inc", "Penn Traffic Co.", "Pennzoil-Quaker State Company", "Pentair Inc", "Peoples Energy Corp.", "PeopleSoft Inc", "Pep Boys Manny, Moe & Jack", "Potomac Electric Power Co.", "Pepsi Bottling Group Inc.", "PepsiAmericas Inc.", "PepsiCo Inc.", "Performance Food Group Co.", "Perini Corp", "PerkinElmer Inc", "Perot Systems Corp", "Petco Animal Supplies Inc.", "Peter Kiewit Sons', Inc.", "PETsMART Inc", "Pfizer Inc", "Pacific Gas & Electric Corp.", "Pharmacia Corp", "Phar Mor Inc.", "Phelps Dodge Corp.", "Philip Morris Companies Inc.", "Phillips Petroleum Co", "Phillips Van Heusen Corp.", "Phoenix Companies Inc", "Pier 1 Imports Inc.", "Pilgrim's Pride Corporation", "Pinnacle West Capital Corp", "Pioneer-Standard Electronics Inc.", "Pitney Bowes Inc.", "Pittston Brinks Group", "Plains All American Pipeline LP", "PNC Financial Services Group Inc.", "PNM Resources Inc", "Polaris Industries Inc.", "Polo Ralph Lauren Corp", "PolyOne Corp", "Popular Inc", "Potlatch Corp", "PPG Industries Inc", "PPL Corp", "Praxair Inc", "Precision Castparts Corp", "Premcor Inc.", "Pride International Inc", "Primedia Inc", "Principal Financial Group Inc.", "Procter & Gamble Co.", "Pro-Fac Cooperative Inc.", "Progress Energy Inc", "Progressive Corporation", "Protective Life Corp", "Provident Financial Group", "Providian Financial Corp.", "Prudential Financial Inc.", "PSS World Medical Inc", "Public Service Enterprise Group Inc.", "Publix Super Markets Inc.", "Puget Energy Inc.", "Pulte Homes Inc", "Qualcomm Inc", "Quanta Services Inc.", "Quantum Corp", "Quest Diagnostics Inc.", "Questar Corp", "Quintiles Transnational", "Qwest Communications Intl Inc", "R.J. Reynolds Tobacco Company", "R.R. Donnelley & Sons Company", "Radio Shack Corporation", "Raymond James Financial Inc.", "Raytheon Company", "Reader's Digest Association Inc.", "Reebok International Ltd.", "Regions Financial Corp.", "Regis Corporation", "Reliance Steel & Aluminum Co.", "Reliant Energy Inc.", "Rent A Center Inc", "Republic Services Inc", "Revlon Inc", "RGS Energy Group Inc", "Rite Aid Corp", "Riverwood Holding Inc.", "RoadwayCorp", "Robert Half International Inc.", "Rock-Tenn Co", "Rockwell Automation Inc", "Rockwell Collins Inc", "Rohm & Haas Co.", "Ross Stores Inc", "RPM Inc.", "Ruddick Corp", "Ryder System Inc", "Ryerson Tull Inc", "Ryland Group Inc.", "Sabre Holdings Corp", "Safeco Corp", "Safeguard Scientifics Inc.", "Safeway Inc", "Saks Inc", "Sanmina-SCI Inc", "Sara Lee Corp", "SBC Communications Inc", "Scana Corp.", "Schering-Plough Corp", "Scholastic Corp", "SCI Systems Onc.", "Science Applications Intl. Inc.", "Scientific-Atlanta Inc", "Scotts Company", "Seaboard Corp", "Sealed Air Corp", "Sears Roebuck & Co", "Sempra Energy", "Sequa Corp", "Service Corp. International", "ServiceMaster Co", "Shaw Group Inc", "Sherwin-Williams Company", "Shopko Stores Inc", "Siebel Systems Inc", "Sierra Health Services Inc", "Sierra Pacific Resources", "Silgan Holdings Inc.", "Silicon Graphics Inc", "Simon Property Group Inc", "SLM Corporation", "Smith International Inc", "Smithfield Foods Inc", "Smurfit-Stone Container Corp", "Snap-On Inc", "Solectron Corp", "Solutia Inc", "Sonic Automotive Inc.", "Sonoco Products Co.", "Southern Company", "Southern Union Company", "SouthTrust Corp.", "Southwest Airlines Co", "Southwest Gas Corp", "Sovereign Bancorp Inc.", "Spartan Stores Inc", "Spherion Corp", "Sports Authority Inc", "Sprint Corp.", "SPX Corp", "St. Jude Medical Inc", "St. Paul Cos.", "Staff Leasing Inc.", "StanCorp Financial Group Inc", "Standard Pacific Corp.", "Stanley Works", "Staples Inc", "Starbucks Corp", "Starwood Hotels & Resorts Worldwide Inc", "State Street Corp.", "Stater Bros. Holdings Inc.", "Steelcase Inc", "Stein Mart Inc", "Stewart & Stevenson Services Inc", "Stewart Information Services Corp", "Stilwell Financial Inc", "Storage Technology Corporation", "Stryker Corp", "Sun Healthcare Group Inc.", "Sun Microsystems Inc.", "SunGard Data Systems Inc.", "Sunoco Inc.", "SunTrust Banks Inc", "Supervalu Inc", "Swift Transportation, Co., Inc", "Symbol Technologies Inc", "Synovus Financial Corp.", "Sysco Corp", "Systemax Inc.", "Target Corp.", "Tech Data Corporation", "TECO Energy Inc", "Tecumseh Products Company", "Tektronix Inc", "Teleflex Incorporated", "Telephone & Data Systems Inc", "Tellabs Inc.", "Temple-Inland Inc", "Tenet Healthcare Corporation", "Tenneco Automotive Inc.", "Teradyne Inc", "Terex Corp", "Tesoro Petroleum Corp.", "Texas Industries Inc.", "Texas Instruments Incorporated", "Textron Inc", "Thermo Electron Corporation", "Thomas & Betts Corporation", "Tiffany & Co", "Timken Company", "TJX Companies Inc", "TMP Worldwide Inc", "Toll Brothers Inc", "Torchmark Corporation", "Toro Company", "Tower Automotive Inc.", "Toys 'R' Us Inc", "Trans World Entertainment Corp.", "TransMontaigne Inc", "Transocean Inc", "TravelCenters of America Inc.", "Triad Hospitals Inc", "Tribune Company", "Trigon Healthcare Inc.", "Trinity Industries Inc", "Trump Hotels & Casino Resorts Inc.", "TruServ Corporation", "TRW Inc", "TXU Corp", "Tyson Foods Inc", "U.S. Bancorp", "U.S. Industries Inc.", "UAL Corporation", "UGI Corporation", "Unified Western Grocers Inc", "Union Pacific Corporation", "Union Planters Corp", "Unisource Energy Corp", "Unisys Corporation", "United Auto Group Inc", "United Defense Industries Inc.", "United Parcel Service Inc", "United Rentals Inc", "United Stationers Inc", "United Technologies Corporation", "UnitedHealth Group Incorporated", "Unitrin Inc", "Universal Corporation", "Universal Forest Products Inc", "Universal Health Services Inc", "Unocal Corporation", "Unova Inc", "UnumProvident Corporation", "URS Corporation", "US Airways Group Inc", "US Oncology Inc", "USA Interactive", "USFreighways Corporation", "USG Corporation", "UST Inc", "Valero Energy Corporation", "Valspar Corporation", "Value City Department Stores Inc", "Varco International Inc", "Vectren Corporation", "Veritas Software Corporation", "Verizon Communications Inc", "VF Corporation", "Viacom Inc", "Viad Corp", "Viasystems Group Inc", "Vishay Intertechnology Inc", "Visteon Corporation", "Volt Information Sciences Inc", "Vulcan Materials Company", "W.R. Berkley Corporation", "W.R. Grace & Co", "W.W. Grainger Inc", "Wachovia Corporation", "Wakenhut Corporation", "Walgreen Co", "Wallace Computer Services Inc", "Wal-Mart Stores Inc", "Walt Disney Co", "Walter Industries Inc", "Washington Mutual Inc", "Washington Post Co.", "Waste Management Inc", "Watsco Inc", "Weatherford International Inc", "Weis Markets Inc.", "Wellpoint Health Networks Inc", "Wells Fargo & Company", "Wendy's International Inc", "Werner Enterprises Inc", "WESCO International Inc", "Western Digital Inc", "Western Gas Resources Inc", "WestPoint Stevens Inc", "Weyerhauser Company", "WGL Holdings Inc", "Whirlpool Corporation", "Whole Foods Market Inc", "Willamette Industries Inc.", "Williams Companies Inc", "Williams Sonoma Inc", "Winn Dixie Stores Inc", "Wisconsin Energy Corporation", "Wm Wrigley Jr Company", "World Fuel Services Corporation", "WorldCom Inc", "Worthington Industries Inc", "WPS Resources Corporation", "Wyeth", "Wyndham International Inc", "Xcel Energy Inc", "Xerox Corp", "Xilinx Inc", "XO Communications Inc", "Yellow Corporation", "York International Corp", "Yum Brands Inc.", "Zale Corporation", "Zions Bancorporation" ], fileExtension : { "raster" : ["bmp", "gif", "gpl", "ico", "jpeg", "psd", "png", "psp", "raw", "tiff"], "vector" : ["3dv", "amf", "awg", "ai", "cgm", "cdr", "cmx", "dxf", "e2d", "egt", "eps", "fs", "odg", "svg", "xar"], "3d" : ["3dmf", "3dm", "3mf", "3ds", "an8", "aoi", "blend", "cal3d", "cob", "ctm", "iob", "jas", "max", "mb", "mdx", "obj", "x", "x3d"], "document" : ["doc", "docx", "dot", "html", "xml", "odt", "odm", "ott", "csv", "rtf", "tex", "xhtml", "xps"] }, // Data taken from https://github.com/dmfilipenko/timezones.json/blob/master/timezones.json timezones: [ { "name": "Dateline Standard Time", "abbr": "DST", "offset": -12, "isdst": false, "text": "(UTC-12:00) International Date Line West", "utc": [ "Etc/GMT+12" ] }, { "name": "UTC-11", "abbr": "U", "offset": -11, "isdst": false, "text": "(UTC-11:00) Coordinated Universal Time-11", "utc": [ "Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago" ] }, { "name": "Hawaiian Standard Time", "abbr": "HST", "offset": -10, "isdst": false, "text": "(UTC-10:00) Hawaii", "utc": [ "Etc/GMT+10", "Pacific/Honolulu", "Pacific/Johnston", "Pacific/Rarotonga", "Pacific/Tahiti" ] }, { "name": "Alaskan Standard Time", "abbr": "AKDT", "offset": -8, "isdst": true, "text": "(UTC-09:00) Alaska", "utc": [ "America/Anchorage", "America/Juneau", "America/Nome", "America/Sitka", "America/Yakutat" ] }, { "name": "Pacific Standard Time (Mexico)", "abbr": "PDT", "offset": -7, "isdst": true, "text": "(UTC-08:00) Baja California", "utc": [ "America/Santa_Isabel" ] }, { "name": "Pacific Standard Time", "abbr": "PDT", "offset": -7, "isdst": true, "text": "(UTC-08:00) Pacific Time (US & Canada)", "utc": [ "America/Dawson", "America/Los_Angeles", "America/Tijuana", "America/Vancouver", "America/Whitehorse", "PST8PDT" ] }, { "name": "US Mountain Standard Time", "abbr": "UMST", "offset": -7, "isdst": false, "text": "(UTC-07:00) Arizona", "utc": [ "America/Creston", "America/Dawson_Creek", "America/Hermosillo", "America/Phoenix", "Etc/GMT+7" ] }, { "name": "Mountain Standard Time (Mexico)", "abbr": "MDT", "offset": -6, "isdst": true, "text": "(UTC-07:00) Chihuahua, La Paz, Mazatlan", "utc": [ "America/Chihuahua", "America/Mazatlan" ] }, { "name": "Mountain Standard Time", "abbr": "MDT", "offset": -6, "isdst": true, "text": "(UTC-07:00) Mountain Time (US & Canada)", "utc": [ "America/Boise", "America/Cambridge_Bay", "America/Denver", "America/Edmonton", "America/Inuvik", "America/Ojinaga", "America/Yellowknife", "MST7MDT" ] }, { "name": "Central America Standard Time", "abbr": "CAST", "offset": -6, "isdst": false, "text": "(UTC-06:00) Central America", "utc": [ "America/Belize", "America/Costa_Rica", "America/El_Salvador", "America/Guatemala", "America/Managua", "America/Tegucigalpa", "Etc/GMT+6", "Pacific/Galapagos" ] }, { "name": "Central Standard Time", "abbr": "CDT", "offset": -5, "isdst": true, "text": "(UTC-06:00) Central Time (US & Canada)", "utc": [ "America/Chicago", "America/Indiana/Knox", "America/Indiana/Tell_City", "America/Matamoros", "America/Menominee", "America/North_Dakota/Beulah", "America/North_Dakota/Center", "America/North_Dakota/New_Salem", "America/Rainy_River", "America/Rankin_Inlet", "America/Resolute", "America/Winnipeg", "CST6CDT" ] }, { "name": "Central Standard Time (Mexico)", "abbr": "CDT", "offset": -5, "isdst": true, "text": "(UTC-06:00) Guadalajara, Mexico City, Monterrey", "utc": [ "America/Bahia_Banderas", "America/Cancun", "America/Merida", "America/Mexico_City", "America/Monterrey" ] }, { "name": "Canada Central Standard Time", "abbr": "CCST", "offset": -6, "isdst": false, "text": "(UTC-06:00) Saskatchewan", "utc": [ "America/Regina", "America/Swift_Current" ] }, { "name": "SA Pacific Standard Time", "abbr": "SPST", "offset": -5, "isdst": false, "text": "(UTC-05:00) Bogota, Lima, Quito", "utc": [ "America/Bogota", "America/Cayman", "America/Coral_Harbour", "America/Eirunepe", "America/Guayaquil", "America/Jamaica", "America/Lima", "America/Panama", "America/Rio_Branco", "Etc/GMT+5" ] }, { "name": "Eastern Standard Time", "abbr": "EDT", "offset": -4, "isdst": true, "text": "(UTC-05:00) Eastern Time (US & Canada)", "utc": [ "America/Detroit", "America/Havana", "America/Indiana/Petersburg", "America/Indiana/Vincennes", "America/Indiana/Winamac", "America/Iqaluit", "America/Kentucky/Monticello", "America/Louisville", "America/Montreal", "America/Nassau", "America/New_York", "America/Nipigon", "America/Pangnirtung", "America/Port-au-Prince", "America/Thunder_Bay", "America/Toronto", "EST5EDT" ] }, { "name": "US Eastern Standard Time", "abbr": "UEDT", "offset": -4, "isdst": true, "text": "(UTC-05:00) Indiana (East)", "utc": [ "America/Indiana/Marengo", "America/Indiana/Vevay", "America/Indianapolis" ] }, { "name": "Venezuela Standard Time", "abbr": "VST", "offset": -4.5, "isdst": false, "text": "(UTC-04:30) Caracas", "utc": [ "America/Caracas" ] }, { "name": "Paraguay Standard Time", "abbr": "PST", "offset": -4, "isdst": false, "text": "(UTC-04:00) Asuncion", "utc": [ "America/Asuncion" ] }, { "name": "Atlantic Standard Time", "abbr": "ADT", "offset": -3, "isdst": true, "text": "(UTC-04:00) Atlantic Time (Canada)", "utc": [ "America/Glace_Bay", "America/Goose_Bay", "America/Halifax", "America/Moncton", "America/Thule", "Atlantic/Bermuda" ] }, { "name": "Central Brazilian Standard Time", "abbr": "CBST", "offset": -4, "isdst": false, "text": "(UTC-04:00) Cuiaba", "utc": [ "America/Campo_Grande", "America/Cuiaba" ] }, { "name": "SA Western Standard Time", "abbr": "SWST", "offset": -4, "isdst": false, "text": "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", "utc": [ "America/Anguilla", "America/Antigua", "America/Aruba", "America/Barbados", "America/Blanc-Sablon", "America/Boa_Vista", "America/Curacao", "America/Dominica", "America/Grand_Turk", "America/Grenada", "America/Guadeloupe", "America/Guyana", "America/Kralendijk", "America/La_Paz", "America/Lower_Princes", "America/Manaus", "America/Marigot", "America/Martinique", "America/Montserrat", "America/Port_of_Spain", "America/Porto_Velho", "America/Puerto_Rico", "America/Santo_Domingo", "America/St_Barthelemy", "America/St_Kitts", "America/St_Lucia", "America/St_Thomas", "America/St_Vincent", "America/Tortola", "Etc/GMT+4" ] }, { "name": "Pacific SA Standard Time", "abbr": "PSST", "offset": -4, "isdst": false, "text": "(UTC-04:00) Santiago", "utc": [ "America/Santiago", "Antarctica/Palmer" ] }, { "name": "Newfoundland Standard Time", "abbr": "NDT", "offset": -2.5, "isdst": true, "text": "(UTC-03:30) Newfoundland", "utc": [ "America/St_Johns" ] }, { "name": "E. South America Standard Time", "abbr": "ESAST", "offset": -3, "isdst": false, "text": "(UTC-03:00) Brasilia", "utc": [ "America/Sao_Paulo" ] }, { "name": "Argentina Standard Time", "abbr": "AST", "offset": -3, "isdst": false, "text": "(UTC-03:00) Buenos Aires", "utc": [ "America/Argentina/La_Rioja", "America/Argentina/Rio_Gallegos", "America/Argentina/Salta", "America/Argentina/San_Juan", "America/Argentina/San_Luis", "America/Argentina/Tucuman", "America/Argentina/Ushuaia", "America/Buenos_Aires", "America/Catamarca", "America/Cordoba", "America/Jujuy", "America/Mendoza" ] }, { "name": "SA Eastern Standard Time", "abbr": "SEST", "offset": -3, "isdst": false, "text": "(UTC-03:00) Cayenne, Fortaleza", "utc": [ "America/Araguaina", "America/Belem", "America/Cayenne", "America/Fortaleza", "America/Maceio", "America/Paramaribo", "America/Recife", "America/Santarem", "Antarctica/Rothera", "Atlantic/Stanley", "Etc/GMT+3" ] }, { "name": "Greenland Standard Time", "abbr": "GDT", "offset": -2, "isdst": true, "text": "(UTC-03:00) Greenland", "utc": [ "America/Godthab" ] }, { "name": "Montevideo Standard Time", "abbr": "MST", "offset": -3, "isdst": false, "text": "(UTC-03:00) Montevideo", "utc": [ "America/Montevideo" ] }, { "name": "Bahia Standard Time", "abbr": "BST", "offset": -3, "isdst": false, "text": "(UTC-03:00) Salvador", "utc": [ "America/Bahia" ] }, { "name": "UTC-02", "abbr": "U", "offset": -2, "isdst": false, "text": "(UTC-02:00) Coordinated Universal Time-02", "utc": [ "America/Noronha", "Atlantic/South_Georgia", "Etc/GMT+2" ] }, { "name": "Mid-Atlantic Standard Time", "abbr": "MDT", "offset": -1, "isdst": true, "text": "(UTC-02:00) Mid-Atlantic - Old" }, { "name": "Azores Standard Time", "abbr": "ADT", "offset": 0, "isdst": true, "text": "(UTC-01:00) Azores", "utc": [ "America/Scoresbysund", "Atlantic/Azores" ] }, { "name": "Cape Verde Standard Time", "abbr": "CVST", "offset": -1, "isdst": false, "text": "(UTC-01:00) Cape Verde Is.", "utc": [ "Atlantic/Cape_Verde", "Etc/GMT+1" ] }, { "name": "Morocco Standard Time", "abbr": "MDT", "offset": 1, "isdst": true, "text": "(UTC) Casablanca", "utc": [ "Africa/Casablanca", "Africa/El_Aaiun" ] }, { "name": "UTC", "abbr": "CUT", "offset": 0, "isdst": false, "text": "(UTC) Coordinated Universal Time", "utc": [ "America/Danmarkshavn", "Etc/GMT" ] }, { "name": "GMT Standard Time", "abbr": "GDT", "offset": 1, "isdst": true, "text": "(UTC) Dublin, Edinburgh, Lisbon, London", "utc": [ "Atlantic/Canary", "Atlantic/Faeroe", "Atlantic/Madeira", "Europe/Dublin", "Europe/Guernsey", "Europe/Isle_of_Man", "Europe/Jersey", "Europe/Lisbon", "Europe/London" ] }, { "name": "Greenwich Standard Time", "abbr": "GST", "offset": 0, "isdst": false, "text": "(UTC) Monrovia, Reykjavik", "utc": [ "Africa/Abidjan", "Africa/Accra", "Africa/Bamako", "Africa/Banjul", "Africa/Bissau", "Africa/Conakry", "Africa/Dakar", "Africa/Freetown", "Africa/Lome", "Africa/Monrovia", "Africa/Nouakchott", "Africa/Ouagadougou", "Africa/Sao_Tome", "Atlantic/Reykjavik", "Atlantic/St_Helena" ] }, { "name": "W. Europe Standard Time", "abbr": "WEDT", "offset": 2, "isdst": true, "text": "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", "utc": [ "Arctic/Longyearbyen", "Europe/Amsterdam", "Europe/Andorra", "Europe/Berlin", "Europe/Busingen", "Europe/Gibraltar", "Europe/Luxembourg", "Europe/Malta", "Europe/Monaco", "Europe/Oslo", "Europe/Rome", "Europe/San_Marino", "Europe/Stockholm", "Europe/Vaduz", "Europe/Vatican", "Europe/Vienna", "Europe/Zurich" ] }, { "name": "Central Europe Standard Time", "abbr": "CEDT", "offset": 2, "isdst": true, "text": "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", "utc": [ "Europe/Belgrade", "Europe/Bratislava", "Europe/Budapest", "Europe/Ljubljana", "Europe/Podgorica", "Europe/Prague", "Europe/Tirane" ] }, { "name": "Romance Standard Time", "abbr": "RDT", "offset": 2, "isdst": true, "text": "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", "utc": [ "Africa/Ceuta", "Europe/Brussels", "Europe/Copenhagen", "Europe/Madrid", "Europe/Paris" ] }, { "name": "Central European Standard Time", "abbr": "CEDT", "offset": 2, "isdst": true, "text": "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", "utc": [ "Europe/Sarajevo", "Europe/Skopje", "Europe/Warsaw", "Europe/Zagreb" ] }, { "name": "W. Central Africa Standard Time", "abbr": "WCAST", "offset": 1, "isdst": false, "text": "(UTC+01:00) West Central Africa", "utc": [ "Africa/Algiers", "Africa/Bangui", "Africa/Brazzaville", "Africa/Douala", "Africa/Kinshasa", "Africa/Lagos", "Africa/Libreville", "Africa/Luanda", "Africa/Malabo", "Africa/Ndjamena", "Africa/Niamey", "Africa/Porto-Novo", "Africa/Tunis", "Etc/GMT-1" ] }, { "name": "Namibia Standard Time", "abbr": "NST", "offset": 1, "isdst": false, "text": "(UTC+01:00) Windhoek", "utc": [ "Africa/Windhoek" ] }, { "name": "GTB Standard Time", "abbr": "GDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Athens, Bucharest", "utc": [ "Asia/Nicosia", "Europe/Athens", "Europe/Bucharest", "Europe/Chisinau" ] }, { "name": "Middle East Standard Time", "abbr": "MEDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Beirut", "utc": [ "Asia/Beirut" ] }, { "name": "Egypt Standard Time", "abbr": "EST", "offset": 2, "isdst": false, "text": "(UTC+02:00) Cairo", "utc": [ "Africa/Cairo" ] }, { "name": "Syria Standard Time", "abbr": "SDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Damascus", "utc": [ "Asia/Damascus" ] }, { "name": "E. Europe Standard Time", "abbr": "EEDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) E. Europe" }, { "name": "South Africa Standard Time", "abbr": "SAST", "offset": 2, "isdst": false, "text": "(UTC+02:00) Harare, Pretoria", "utc": [ "Africa/Blantyre", "Africa/Bujumbura", "Africa/Gaborone", "Africa/Harare", "Africa/Johannesburg", "Africa/Kigali", "Africa/Lubumbashi", "Africa/Lusaka", "Africa/Maputo", "Africa/Maseru", "Africa/Mbabane", "Etc/GMT-2" ] }, { "name": "FLE Standard Time", "abbr": "FDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", "utc": [ "Europe/Helsinki", "Europe/Kiev", "Europe/Mariehamn", "Europe/Riga", "Europe/Sofia", "Europe/Tallinn", "Europe/Uzhgorod", "Europe/Vilnius", "Europe/Zaporozhye" ] }, { "name": "Turkey Standard Time", "abbr": "TDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Istanbul", "utc": [ "Europe/Istanbul" ] }, { "name": "Israel Standard Time", "abbr": "JDT", "offset": 3, "isdst": true, "text": "(UTC+02:00) Jerusalem", "utc": [ "Asia/Jerusalem" ] }, { "name": "Libya Standard Time", "abbr": "LST", "offset": 2, "isdst": false, "text": "(UTC+02:00) Tripoli", "utc": [ "Africa/Tripoli" ] }, { "name": "Jordan Standard Time", "abbr": "JST", "offset": 3, "isdst": false, "text": "(UTC+03:00) Amman", "utc": [ "Asia/Amman" ] }, { "name": "Arabic Standard Time", "abbr": "AST", "offset": 3, "isdst": false, "text": "(UTC+03:00) Baghdad", "utc": [ "Asia/Baghdad" ] }, { "name": "Kaliningrad Standard Time", "abbr": "KST", "offset": 3, "isdst": false, "text": "(UTC+03:00) Kaliningrad, Minsk", "utc": [ "Europe/Kaliningrad", "Europe/Minsk" ] }, { "name": "Arab Standard Time", "abbr": "AST", "offset": 3, "isdst": false, "text": "(UTC+03:00) Kuwait, Riyadh", "utc": [ "Asia/Aden", "Asia/Bahrain", "Asia/Kuwait", "Asia/Qatar", "Asia/Riyadh" ] }, { "name": "E. Africa Standard Time", "abbr": "EAST", "offset": 3, "isdst": false, "text": "(UTC+03:00) Nairobi", "utc": [ "Africa/Addis_Ababa", "Africa/Asmera", "Africa/Dar_es_Salaam", "Africa/Djibouti", "Africa/Juba", "Africa/Kampala", "Africa/Khartoum", "Africa/Mogadishu", "Africa/Nairobi", "Antarctica/Syowa", "Etc/GMT-3", "Indian/Antananarivo", "Indian/Comoro", "Indian/Mayotte" ] }, { "name": "Iran Standard Time", "abbr": "IDT", "offset": 4.5, "isdst": true, "text": "(UTC+03:30) Tehran", "utc": [ "Asia/Tehran" ] }, { "name": "Arabian Standard Time", "abbr": "AST", "offset": 4, "isdst": false, "text": "(UTC+04:00) Abu Dhabi, Muscat", "utc": [ "Asia/Dubai", "Asia/Muscat", "Etc/GMT-4" ] }, { "name": "Azerbaijan Standard Time", "abbr": "ADT", "offset": 5, "isdst": true, "text": "(UTC+04:00) Baku", "utc": [ "Asia/Baku" ] }, { "name": "Russian Standard Time", "abbr": "RST", "offset": 4, "isdst": false, "text": "(UTC+04:00) Moscow, St. Petersburg, Volgograd", "utc": [ "Europe/Moscow", "Europe/Samara", "Europe/Simferopol", "Europe/Volgograd" ] }, { "name": "Mauritius Standard Time", "abbr": "MST", "offset": 4, "isdst": false, "text": "(UTC+04:00) Port Louis", "utc": [ "Indian/Mahe", "Indian/Mauritius", "Indian/Reunion" ] }, { "name": "Georgian Standard Time", "abbr": "GST", "offset": 4, "isdst": false, "text": "(UTC+04:00) Tbilisi", "utc": [ "Asia/Tbilisi" ] }, { "name": "Caucasus Standard Time", "abbr": "CST", "offset": 4, "isdst": false, "text": "(UTC+04:00) Yerevan", "utc": [ "Asia/Yerevan" ] }, { "name": "Afghanistan Standard Time", "abbr": "AST", "offset": 4.5, "isdst": false, "text": "(UTC+04:30) Kabul", "utc": [ "Asia/Kabul" ] }, { "name": "West Asia Standard Time", "abbr": "WAST", "offset": 5, "isdst": false, "text": "(UTC+05:00) Ashgabat, Tashkent", "utc": [ "Antarctica/Mawson", "Asia/Aqtau", "Asia/Aqtobe", "Asia/Ashgabat", "Asia/Dushanbe", "Asia/Oral", "Asia/Samarkand", "Asia/Tashkent", "Etc/GMT-5", "Indian/Kerguelen", "Indian/Maldives" ] }, { "name": "Pakistan Standard Time", "abbr": "PST", "offset": 5, "isdst": false, "text": "(UTC+05:00) Islamabad, Karachi", "utc": [ "Asia/Karachi" ] }, { "name": "India Standard Time", "abbr": "IST", "offset": 5.5, "isdst": false, "text": "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", "utc": [ "Asia/Calcutta" ] }, { "name": "Sri Lanka Standard Time", "abbr": "SLST", "offset": 5.5, "isdst": false, "text": "(UTC+05:30) Sri Jayawardenepura", "utc": [ "Asia/Colombo" ] }, { "name": "Nepal Standard Time", "abbr": "NST", "offset": 5.75, "isdst": false, "text": "(UTC+05:45) Kathmandu", "utc": [ "Asia/Katmandu" ] }, { "name": "Central Asia Standard Time", "abbr": "CAST", "offset": 6, "isdst": false, "text": "(UTC+06:00) Astana", "utc": [ "Antarctica/Vostok", "Asia/Almaty", "Asia/Bishkek", "Asia/Qyzylorda", "Asia/Urumqi", "Etc/GMT-6", "Indian/Chagos" ] }, { "name": "Bangladesh Standard Time", "abbr": "BST", "offset": 6, "isdst": false, "text": "(UTC+06:00) Dhaka", "utc": [ "Asia/Dhaka", "Asia/Thimphu" ] }, { "name": "Ekaterinburg Standard Time", "abbr": "EST", "offset": 6, "isdst": false, "text": "(UTC+06:00) Ekaterinburg", "utc": [ "Asia/Yekaterinburg" ] }, { "name": "Myanmar Standard Time", "abbr": "MST", "offset": 6.5, "isdst": false, "text": "(UTC+06:30) Yangon (Rangoon)", "utc": [ "Asia/Rangoon", "Indian/Cocos" ] }, { "name": "SE Asia Standard Time", "abbr": "SAST", "offset": 7, "isdst": false, "text": "(UTC+07:00) Bangkok, Hanoi, Jakarta", "utc": [ "Antarctica/Davis", "Asia/Bangkok", "Asia/Hovd", "Asia/Jakarta", "Asia/Phnom_Penh", "Asia/Pontianak", "Asia/Saigon", "Asia/Vientiane", "Etc/GMT-7", "Indian/Christmas" ] }, { "name": "N. Central Asia Standard Time", "abbr": "NCAST", "offset": 7, "isdst": false, "text": "(UTC+07:00) Novosibirsk", "utc": [ "Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk" ] }, { "name": "China Standard Time", "abbr": "CST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", "utc": [ "Asia/Hong_Kong", "Asia/Macau", "Asia/Shanghai" ] }, { "name": "North Asia Standard Time", "abbr": "NAST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Krasnoyarsk", "utc": [ "Asia/Krasnoyarsk" ] }, { "name": "Singapore Standard Time", "abbr": "MPST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Kuala Lumpur, Singapore", "utc": [ "Asia/Brunei", "Asia/Kuala_Lumpur", "Asia/Kuching", "Asia/Makassar", "Asia/Manila", "Asia/Singapore", "Etc/GMT-8" ] }, { "name": "W. Australia Standard Time", "abbr": "WAST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Perth", "utc": [ "Antarctica/Casey", "Australia/Perth" ] }, { "name": "Taipei Standard Time", "abbr": "TST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Taipei", "utc": [ "Asia/Taipei" ] }, { "name": "Ulaanbaatar Standard Time", "abbr": "UST", "offset": 8, "isdst": false, "text": "(UTC+08:00) Ulaanbaatar", "utc": [ "Asia/Choibalsan", "Asia/Ulaanbaatar" ] }, { "name": "North Asia East Standard Time", "abbr": "NAEST", "offset": 9, "isdst": false, "text": "(UTC+09:00) Irkutsk", "utc": [ "Asia/Irkutsk" ] }, { "name": "Tokyo Standard Time", "abbr": "TST", "offset": 9, "isdst": false, "text": "(UTC+09:00) Osaka, Sapporo, Tokyo", "utc": [ "Asia/Dili", "Asia/Jayapura", "Asia/Tokyo", "Etc/GMT-9", "Pacific/Palau" ] }, { "name": "Korea Standard Time", "abbr": "KST", "offset": 9, "isdst": false, "text": "(UTC+09:00) Seoul", "utc": [ "Asia/Pyongyang", "Asia/Seoul" ] }, { "name": "Cen. Australia Standard Time", "abbr": "CAST", "offset": 9.5, "isdst": false, "text": "(UTC+09:30) Adelaide", "utc": [ "Australia/Adelaide", "Australia/Broken_Hill" ] }, { "name": "AUS Central Standard Time", "abbr": "ACST", "offset": 9.5, "isdst": false, "text": "(UTC+09:30) Darwin", "utc": [ "Australia/Darwin" ] }, { "name": "E. Australia Standard Time", "abbr": "EAST", "offset": 10, "isdst": false, "text": "(UTC+10:00) Brisbane", "utc": [ "Australia/Brisbane", "Australia/Lindeman" ] }, { "name": "AUS Eastern Standard Time", "abbr": "AEST", "offset": 10, "isdst": false, "text": "(UTC+10:00) Canberra, Melbourne, Sydney", "utc": [ "Australia/Melbourne", "Australia/Sydney" ] }, { "name": "West Pacific Standard Time", "abbr": "WPST", "offset": 10, "isdst": false, "text": "(UTC+10:00) Guam, Port Moresby", "utc": [ "Antarctica/DumontDUrville", "Etc/GMT-10", "Pacific/Guam", "Pacific/Port_Moresby", "Pacific/Saipan", "Pacific/Truk" ] }, { "name": "Tasmania Standard Time", "abbr": "TST", "offset": 10, "isdst": false, "text": "(UTC+10:00) Hobart", "utc": [ "Australia/Currie", "Australia/Hobart" ] }, { "name": "Yakutsk Standard Time", "abbr": "YST", "offset": 10, "isdst": false, "text": "(UTC+10:00) Yakutsk", "utc": [ "Asia/Chita", "Asia/Khandyga", "Asia/Yakutsk" ] }, { "name": "Central Pacific Standard Time", "abbr": "CPST", "offset": 11, "isdst": false, "text": "(UTC+11:00) Solomon Is., New Caledonia", "utc": [ "Antarctica/Macquarie", "Etc/GMT-11", "Pacific/Efate", "Pacific/Guadalcanal", "Pacific/Kosrae", "Pacific/Noumea", "Pacific/Ponape" ] }, { "name": "Vladivostok Standard Time", "abbr": "VST", "offset": 11, "isdst": false, "text": "(UTC+11:00) Vladivostok", "utc": [ "Asia/Sakhalin", "Asia/Ust-Nera", "Asia/Vladivostok" ] }, { "name": "New Zealand Standard Time", "abbr": "NZST", "offset": 12, "isdst": false, "text": "(UTC+12:00) Auckland, Wellington", "utc": [ "Antarctica/McMurdo", "Pacific/Auckland" ] }, { "name": "UTC+12", "abbr": "U", "offset": 12, "isdst": false, "text": "(UTC+12:00) Coordinated Universal Time+12", "utc": [ "Etc/GMT-12", "Pacific/Funafuti", "Pacific/Kwajalein", "Pacific/Majuro", "Pacific/Nauru", "Pacific/Tarawa", "Pacific/Wake", "Pacific/Wallis" ] }, { "name": "Fiji Standard Time", "abbr": "FST", "offset": 12, "isdst": false, "text": "(UTC+12:00) Fiji", "utc": [ "Pacific/Fiji" ] }, { "name": "Magadan Standard Time", "abbr": "MST", "offset": 12, "isdst": false, "text": "(UTC+12:00) Magadan", "utc": [ "Asia/Anadyr", "Asia/Kamchatka", "Asia/Magadan", "Asia/Srednekolymsk" ] }, { "name": "Kamchatka Standard Time", "abbr": "KDT", "offset": 13, "isdst": true, "text": "(UTC+12:00) Petropavlovsk-Kamchatsky - Old" }, { "name": "Tonga Standard Time", "abbr": "TST", "offset": 13, "isdst": false, "text": "(UTC+13:00) Nuku'alofa", "utc": [ "Etc/GMT-13", "Pacific/Enderbury", "Pacific/Fakaofo", "Pacific/Tongatapu" ] }, { "name": "Samoa Standard Time", "abbr": "SST", "offset": 13, "isdst": false, "text": "(UTC+13:00) Samoa", "utc": [ "Pacific/Apia" ] } ], //List source: http://answers.google.com/answers/threadview/id/589312.html profession: [ "Airline Pilot", "Academic Team", "Accountant", "Account Executive", "Actor", "Actuary", "Acquisition Analyst", "Administrative Asst.", "Administrative Analyst", "Administrator", "Advertising Director", "Aerospace Engineer", "Agent", "Agricultural Inspector", "Agricultural Scientist", "Air Traffic Controller", "Animal Trainer", "Anthropologist", "Appraiser", "Architect", "Art Director", "Artist", "Astronomer", "Athletic Coach", "Auditor", "Author", "Baker", "Banker", "Bankruptcy Attorney", "Benefits Manager", "Biologist", "Bio-feedback Specialist", "Biomedical Engineer", "Biotechnical Researcher", "Broadcaster", "Broker", "Building Manager", "Building Contractor", "Building Inspector", "Business Analyst", "Business Planner", "Business Manager", "Buyer", "Call Center Manager", "Career Counselor", "Cash Manager", "Ceramic Engineer", "Chief Executive Officer", "Chief Operation Officer", "Chef", "Chemical Engineer", "Chemist", "Child Care Manager", "Chief Medical Officer", "Chiropractor", "Cinematographer", "City Housing Manager", "City Manager", "Civil Engineer", "Claims Manager", "Clinical Research Assistant", "Collections Manager.", "Compliance Manager", "Comptroller", "Computer Manager", "Commercial Artist", "Communications Affairs Director", "Communications Director", "Communications Engineer", "Compensation Analyst", "Computer Programmer", "Computer Ops. Manager", "Computer Engineer", "Computer Operator", "Computer Graphics Specialist", "Construction Engineer", "Construction Manager", "Consultant", "Consumer Relations Manager", "Contract Administrator", "Copyright Attorney", "Copywriter", "Corporate Planner", "Corrections Officer", "Cosmetologist", "Credit Analyst", "Cruise Director", "Chief Information Officer", "Chief Technology Officer", "Customer Service Manager", "Cryptologist", "Dancer", "Data Security Manager", "Database Manager", "Day Care Instructor", "Dentist", "Designer", "Design Engineer", "Desktop Publisher", "Developer", "Development Officer", "Diamond Merchant", "Dietitian", "Direct Marketer", "Director", "Distribution Manager", "Diversity Manager", "Economist", "EEO Compliance Manager", "Editor", "Education Adminator", "Electrical Engineer", "Electro Optical Engineer", "Electronics Engineer", "Embassy Management", "Employment Agent", "Engineer Technician", "Entrepreneur", "Environmental Analyst", "Environmental Attorney", "Environmental Engineer", "Environmental Specialist", "Escrow Officer", "Estimator", "Executive Assistant", "Executive Director", "Executive Recruiter", "Facilities Manager", "Family Counselor", "Fashion Events Manager", "Fashion Merchandiser", "Fast Food Manager", "Film Producer", "Film Production Assistant", "Financial Analyst", "Financial Planner", "Financier", "Fine Artist", "Wildlife Specialist", "Fitness Consultant", "Flight Attendant", "Flight Engineer", "Floral Designer", "Food & Beverage Director", "Food Service Manager", "Forestry Technician", "Franchise Management", "Franchise Sales", "Fraud Investigator", "Freelance Writer", "Fund Raiser", "General Manager", "Geologist", "General Counsel", "Geriatric Specialist", "Gerontologist", "Glamour Photographer", "Golf Club Manager", "Gourmet Chef", "Graphic Designer", "Grounds Keeper", "Hazardous Waste Manager", "Health Care Manager", "Health Therapist", "Health Service Administrator", "Hearing Officer", "Home Economist", "Horticulturist", "Hospital Administrator", "Hotel Manager", "Human Resources Manager", "Importer", "Industrial Designer", "Industrial Engineer", "Information Director", "Inside Sales", "Insurance Adjuster", "Interior Decorator", "Internal Controls Director", "International Acct.", "International Courier", "International Lawyer", "Interpreter", "Investigator", "Investment Banker", "Investment Manager", "IT Architect", "IT Project Manager", "IT Systems Analyst", "Jeweler", "Joint Venture Manager", "Journalist", "Labor Negotiator", "Labor Organizer", "Labor Relations Manager", "Lab Services Director", "Lab Technician", "Land Developer", "Landscape Architect", "Law Enforcement Officer", "Lawyer", "Lead Software Engineer", "Lead Software Test Engineer", "Leasing Manager", "Legal Secretary", "Library Manager", "Litigation Attorney", "Loan Officer", "Lobbyist", "Logistics Manager", "Maintenance Manager", "Management Consultant", "Managed Care Director", "Managing Partner", "Manufacturing Director", "Manpower Planner", "Marine Biologist", "Market Res. Analyst", "Marketing Director", "Materials Manager", "Mathematician", "Membership Chairman", "Mechanic", "Mechanical Engineer", "Media Buyer", "Medical Investor", "Medical Secretary", "Medical Technician", "Mental Health Counselor", "Merchandiser", "Metallurgical Engineering", "Meteorologist", "Microbiologist", "MIS Manager", "Motion Picture Director", "Multimedia Director", "Musician", "Network Administrator", "Network Specialist", "Network Operator", "New Product Manager", "Novelist", "Nuclear Engineer", "Nuclear Specialist", "Nutritionist", "Nursing Administrator", "Occupational Therapist", "Oceanographer", "Office Manager", "Operations Manager", "Operations Research Director", "Optical Technician", "Optometrist", "Organizational Development Manager", "Outplacement Specialist", "Paralegal", "Park Ranger", "Patent Attorney", "Payroll Specialist", "Personnel Specialist", "Petroleum Engineer", "Pharmacist", "Photographer", "Physical Therapist", "Physician", "Physician Assistant", "Physicist", "Planning Director", "Podiatrist", "Political Analyst", "Political Scientist", "Politician", "Portfolio Manager", "Preschool Management", "Preschool Teacher", "Principal", "Private Banker", "Private Investigator", "Probation Officer", "Process Engineer", "Producer", "Product Manager", "Product Engineer", "Production Engineer", "Production Planner", "Professional Athlete", "Professional Coach", "Professor", "Project Engineer", "Project Manager", "Program Manager", "Property Manager", "Public Administrator", "Public Safety Director", "PR Specialist", "Publisher", "Purchasing Agent", "Publishing Director", "Quality Assurance Specialist", "Quality Control Engineer", "Quality Control Inspector", "Radiology Manager", "Railroad Engineer", "Real Estate Broker", "Recreational Director", "Recruiter", "Redevelopment Specialist", "Regulatory Affairs Manager", "Registered Nurse", "Rehabilitation Counselor", "Relocation Manager", "Reporter", "Research Specialist", "Restaurant Manager", "Retail Store Manager", "Risk Analyst", "Safety Engineer", "Sales Engineer", "Sales Trainer", "Sales Promotion Manager", "Sales Representative", "Sales Manager", "Service Manager", "Sanitation Engineer", "Scientific Programmer", "Scientific Writer", "Securities Analyst", "Security Consultant", "Security Director", "Seminar Presenter", "Ship's Officer", "Singer", "Social Director", "Social Program Planner", "Social Research", "Social Scientist", "Social Worker", "Sociologist", "Software Developer", "Software Engineer", "Software Test Engineer", "Soil Scientist", "Special Events Manager", "Special Education Teacher", "Special Projects Director", "Speech Pathologist", "Speech Writer", "Sports Event Manager", "Statistician", "Store Manager", "Strategic Alliance Director", "Strategic Planning Director", "Stress Reduction Specialist", "Stockbroker", "Surveyor", "Structural Engineer", "Superintendent", "Supply Chain Director", "System Engineer", "Systems Analyst", "Systems Programmer", "System Administrator", "Tax Specialist", "Teacher", "Technical Support Specialist", "Technical Illustrator", "Technical Writer", "Technology Director", "Telecom Analyst", "Telemarketer", "Theatrical Director", "Title Examiner", "Tour Escort", "Tour Guide Director", "Traffic Manager", "Trainer Translator", "Transportation Manager", "Travel Agent", "Treasurer", "TV Programmer", "Underwriter", "Union Representative", "University Administrator", "University Dean", "Urban Planner", "Veterinarian", "Vendor Relations Director", "Viticulturist", "Warehouse Manager" ] }; var o_hasOwnProperty = Object.prototype.hasOwnProperty; var o_keys = (Object.keys || function(obj) { var result = []; for (var key in obj) { if (o_hasOwnProperty.call(obj, key)) { result.push(key); } } return result; }); function _copyObject(source, target) { var keys = o_keys(source); var key; for (var i = 0, l = keys.length; i < l; i++) { key = keys[i]; target[key] = source[key] || target[key]; } } function _copyArray(source, target) { for (var i = 0, l = source.length; i < l; i++) { target[i] = source[i]; } } function copyObject(source, _target) { var isArray = Array.isArray(source); var target = _target || (isArray ? new Array(source.length) : {}); if (isArray) { _copyArray(source, target); } else { _copyObject(source, target); } return target; } /** Get the data based on key**/ Chance.prototype.get = function (name) { return copyObject(data[name]); }; // Mac Address Chance.prototype.mac_address = function(options){ // typically mac addresses are separated by ":" // however they can also be separated by "-" // the network variant uses a dot every fourth byte options = initOptions(options); if(!options.separator) { options.separator = options.networkVersion ? "." : ":"; } var mac_pool="ABCDEF1234567890", mac = ""; if(!options.networkVersion) { mac = this.n(this.string, 6, { pool: mac_pool, length:2 }).join(options.separator); } else { mac = this.n(this.string, 3, { pool: mac_pool, length:4 }).join(options.separator); } return mac; }; Chance.prototype.normal = function (options) { options = initOptions(options, {mean : 0, dev : 1, pool : []}); testRange( options.pool.constructor !== Array, "Chance: The pool option must be a valid array." ); testRange( typeof options.mean !== 'number', "Chance: Mean (mean) must be a number" ); testRange( typeof options.dev !== 'number', "Chance: Standard deviation (dev) must be a number" ); // If a pool has been passed, then we are returning an item from that pool, // using the normal distribution settings that were passed in if (options.pool.length > 0) { return this.normal_pool(options); } // The Marsaglia Polar method var s, u, v, norm, mean = options.mean, dev = options.dev; do { // U and V are from the uniform distribution on (-1, 1) u = this.random() * 2 - 1; v = this.random() * 2 - 1; s = u * u + v * v; } while (s >= 1); // Compute the standard normal variate norm = u * Math.sqrt(-2 * Math.log(s) / s); // Shape and scale return dev * norm + mean; }; Chance.prototype.normal_pool = function(options) { var performanceCounter = 0; do { var idx = Math.round(this.normal({ mean: options.mean, dev: options.dev })); if (idx < options.pool.length && idx >= 0) { return options.pool[idx]; } else { performanceCounter++; } } while(performanceCounter < 100); throw new RangeError("Chance: Your pool is too small for the given mean and standard deviation. Please adjust."); }; Chance.prototype.radio = function (options) { // Initial Letter (Typically Designated by Side of Mississippi River) options = initOptions(options, {side : "?"}); var fl = ""; switch (options.side.toLowerCase()) { case "east": case "e": fl = "W"; break; case "west": case "w": fl = "K"; break; default: fl = this.character({pool: "KW"}); break; } return fl + this.character({alpha: true, casing: "upper"}) + this.character({alpha: true, casing: "upper"}) + this.character({alpha: true, casing: "upper"}); }; // Set the data as key and data or the data map Chance.prototype.set = function (name, values) { if (typeof name === "string") { data[name] = values; } else { data = copyObject(name, data); } }; Chance.prototype.tv = function (options) { return this.radio(options); }; // ID number for Brazil companies Chance.prototype.cnpj = function () { var n = this.n(this.natural, 8, { max: 9 }); var d1 = 2+n[7]*6+n[6]*7+n[5]*8+n[4]*9+n[3]*2+n[2]*3+n[1]*4+n[0]*5; d1 = 11 - (d1 % 11); if (d1>=10){ d1 = 0; } var d2 = d1*2+3+n[7]*7+n[6]*8+n[5]*9+n[4]*2+n[3]*3+n[2]*4+n[1]*5+n[0]*6; d2 = 11 - (d2 % 11); if (d2>=10){ d2 = 0; } return ''+n[0]+n[1]+'.'+n[2]+n[3]+n[4]+'.'+n[5]+n[6]+n[7]+'/0001-'+d1+d2; }; // -- End Miscellaneous -- Chance.prototype.mersenne_twister = function (seed) { return new MersenneTwister(seed); }; Chance.prototype.blueimp_md5 = function () { return new BlueImpMD5(); }; // Mersenne Twister from https://gist.github.com/banksean/300494 /* A C-program for MT19937, with initialization improved 2002/1/26. Coded by Takuji Nishimura and Makoto Matsumoto. Before using, initialize the state by using init_genrand(seed) or init_by_array(init_key, key_length). Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Any feedback is very welcome. http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) */ var MersenneTwister = function (seed) { if (seed === undefined) { // kept random number same size as time used previously to ensure no unexpected results downstream seed = Math.floor(Math.random()*Math.pow(10,13)); } /* Period parameters */ this.N = 624; this.M = 397; this.MATRIX_A = 0x9908b0df; /* constant vector a */ this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ this.mt = new Array(this.N); /* the array for the state vector */ this.mti = this.N + 1; /* mti==N + 1 means mt[N] is not initialized */ this.init_genrand(seed); }; /* initializes mt[N] with a seed */ MersenneTwister.prototype.init_genrand = function (s) { this.mt[0] = s >>> 0; for (this.mti = 1; this.mti < this.N; this.mti++) { s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30); this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ /* In the previous versions, MSBs of the seed affect */ /* only MSBs of the array mt[]. */ /* 2002/01/09 modified by Makoto Matsumoto */ this.mt[this.mti] >>>= 0; /* for >32 bit machines */ } }; /* initialize by an array with array-length */ /* init_key is the array for initializing keys */ /* key_length is its length */ /* slight change for C++, 2004/2/26 */ MersenneTwister.prototype.init_by_array = function (init_key, key_length) { var i = 1, j = 0, k, s; this.init_genrand(19650218); k = (this.N > key_length ? this.N : key_length); for (; k; k--) { s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + init_key[j] + j; /* non linear */ this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ i++; j++; if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; } if (j >= key_length) { j = 0; } } for (k = this.N - 1; k; k--) { s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; /* non linear */ this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ i++; if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; } } this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ }; /* generates a random number on [0,0xffffffff]-interval */ MersenneTwister.prototype.genrand_int32 = function () { var y; var mag01 = new Array(0x0, this.MATRIX_A); /* mag01[x] = x * MATRIX_A for x=0,1 */ if (this.mti >= this.N) { /* generate N words at one time */ var kk; if (this.mti === this.N + 1) { /* if init_genrand() has not been called, */ this.init_genrand(5489); /* a default initial seed is used */ } for (kk = 0; kk < this.N - this.M; kk++) { y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK); this.mt[kk] = this.mt[kk + this.M] ^ (y >>> 1) ^ mag01[y & 0x1]; } for (;kk < this.N - 1; kk++) { y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK); this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ (y >>> 1) ^ mag01[y & 0x1]; } y = (this.mt[this.N - 1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); this.mt[this.N - 1] = this.mt[this.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1]; this.mti = 0; } y = this.mt[this.mti++]; /* Tempering */ y ^= (y >>> 11); y ^= (y << 7) & 0x9d2c5680; y ^= (y << 15) & 0xefc60000; y ^= (y >>> 18); return y >>> 0; }; /* generates a random number on [0,0x7fffffff]-interval */ MersenneTwister.prototype.genrand_int31 = function () { return (this.genrand_int32() >>> 1); }; /* generates a random number on [0,1]-real-interval */ MersenneTwister.prototype.genrand_real1 = function () { return this.genrand_int32() * (1.0 / 4294967295.0); /* divided by 2^32-1 */ }; /* generates a random number on [0,1)-real-interval */ MersenneTwister.prototype.random = function () { return this.genrand_int32() * (1.0 / 4294967296.0); /* divided by 2^32 */ }; /* generates a random number on (0,1)-real-interval */ MersenneTwister.prototype.genrand_real3 = function () { return (this.genrand_int32() + 0.5) * (1.0 / 4294967296.0); /* divided by 2^32 */ }; /* generates a random number on [0,1) with 53-bit resolution*/ MersenneTwister.prototype.genrand_res53 = function () { var a = this.genrand_int32()>>>5, b = this.genrand_int32()>>>6; return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); }; // BlueImp MD5 hashing algorithm from https://github.com/blueimp/JavaScript-MD5 var BlueImpMD5 = function () {}; BlueImpMD5.prototype.VERSION = '1.0.1'; /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ BlueImpMD5.prototype.safe_add = 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. */ BlueImpMD5.prototype.bit_roll = function (num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); }; /* * These functions implement the five basic operations the algorithm uses. */ BlueImpMD5.prototype.md5_cmn = function (q, a, b, x, s, t) { return this.safe_add(this.bit_roll(this.safe_add(this.safe_add(a, q), this.safe_add(x, t)), s), b); }; BlueImpMD5.prototype.md5_ff = function (a, b, c, d, x, s, t) { return this.md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); }; BlueImpMD5.prototype.md5_gg = function (a, b, c, d, x, s, t) { return this.md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); }; BlueImpMD5.prototype.md5_hh = function (a, b, c, d, x, s, t) { return this.md5_cmn(b ^ c ^ d, a, b, x, s, t); }; BlueImpMD5.prototype.md5_ii = function (a, b, c, d, x, s, t) { return this.md5_cmn(c ^ (b | (~d)), a, b, x, s, t); }; /* * Calculate the MD5 of an array of little-endian words, and a bit length. */ BlueImpMD5.prototype.binl_md5 = function (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 = this.md5_ff(a, b, c, d, x[i], 7, -680876936); d = this.md5_ff(d, a, b, c, x[i + 1], 12, -389564586); c = this.md5_ff(c, d, a, b, x[i + 2], 17, 606105819); b = this.md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); a = this.md5_ff(a, b, c, d, x[i + 4], 7, -176418897); d = this.md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); c = this.md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); b = this.md5_ff(b, c, d, a, x[i + 7], 22, -45705983); a = this.md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); d = this.md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); c = this.md5_ff(c, d, a, b, x[i + 10], 17, -42063); b = this.md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); a = this.md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); d = this.md5_ff(d, a, b, c, x[i + 13], 12, -40341101); c = this.md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); b = this.md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); a = this.md5_gg(a, b, c, d, x[i + 1], 5, -165796510); d = this.md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); c = this.md5_gg(c, d, a, b, x[i + 11], 14, 643717713); b = this.md5_gg(b, c, d, a, x[i], 20, -373897302); a = this.md5_gg(a, b, c, d, x[i + 5], 5, -701558691); d = this.md5_gg(d, a, b, c, x[i + 10], 9, 38016083); c = this.md5_gg(c, d, a, b, x[i + 15], 14, -660478335); b = this.md5_gg(b, c, d, a, x[i + 4], 20, -405537848); a = this.md5_gg(a, b, c, d, x[i + 9], 5, 568446438); d = this.md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); c = this.md5_gg(c, d, a, b, x[i + 3], 14, -187363961); b = this.md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); a = this.md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); d = this.md5_gg(d, a, b, c, x[i + 2], 9, -51403784); c = this.md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); b = this.md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); a = this.md5_hh(a, b, c, d, x[i + 5], 4, -378558); d = this.md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); c = this.md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); b = this.md5_hh(b, c, d, a, x[i + 14], 23, -35309556); a = this.md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); d = this.md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); c = this.md5_hh(c, d, a, b, x[i + 7], 16, -155497632); b = this.md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); a = this.md5_hh(a, b, c, d, x[i + 13], 4, 681279174); d = this.md5_hh(d, a, b, c, x[i], 11, -358537222); c = this.md5_hh(c, d, a, b, x[i + 3], 16, -722521979); b = this.md5_hh(b, c, d, a, x[i + 6], 23, 76029189); a = this.md5_hh(a, b, c, d, x[i + 9], 4, -640364487); d = this.md5_hh(d, a, b, c, x[i + 12], 11, -421815835); c = this.md5_hh(c, d, a, b, x[i + 15], 16, 530742520); b = this.md5_hh(b, c, d, a, x[i + 2], 23, -995338651); a = this.md5_ii(a, b, c, d, x[i], 6, -198630844); d = this.md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); c = this.md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); b = this.md5_ii(b, c, d, a, x[i + 5], 21, -57434055); a = this.md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); d = this.md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); c = this.md5_ii(c, d, a, b, x[i + 10], 15, -1051523); b = this.md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); a = this.md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); d = this.md5_ii(d, a, b, c, x[i + 15], 10, -30611744); c = this.md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); b = this.md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); a = this.md5_ii(a, b, c, d, x[i + 4], 6, -145523070); d = this.md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); c = this.md5_ii(c, d, a, b, x[i + 2], 15, 718787259); b = this.md5_ii(b, c, d, a, x[i + 9], 21, -343485551); a = this.safe_add(a, olda); b = this.safe_add(b, oldb); c = this.safe_add(c, oldc); d = this.safe_add(d, oldd); } return [a, b, c, d]; }; /* * Convert an array of little-endian words to a string */ BlueImpMD5.prototype.binl2rstr = function (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. */ BlueImpMD5.prototype.rstr2binl = function (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 */ BlueImpMD5.prototype.rstr_md5 = function (s) { return this.binl2rstr(this.binl_md5(this.rstr2binl(s), s.length * 8)); }; /* * Calculate the HMAC-MD5, of a key and some data (raw strings) */ BlueImpMD5.prototype.rstr_hmac_md5 = function (key, data) { var i, bkey = this.rstr2binl(key), ipad = [], opad = [], hash; ipad[15] = opad[15] = undefined; if (bkey.length > 16) { bkey = this.binl_md5(bkey, key.length * 8); } for (i = 0; i < 16; i += 1) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } hash = this.binl_md5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8); return this.binl2rstr(this.binl_md5(opad.concat(hash), 512 + 128)); }; /* * Convert a raw string to a hex string */ BlueImpMD5.prototype.rstr2hex = function (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 */ BlueImpMD5.prototype.str2rstr_utf8 = function (input) { return unescape(encodeURIComponent(input)); }; /* * Take string arguments and return either raw or hex encoded strings */ BlueImpMD5.prototype.raw_md5 = function (s) { return this.rstr_md5(this.str2rstr_utf8(s)); }; BlueImpMD5.prototype.hex_md5 = function (s) { return this.rstr2hex(this.raw_md5(s)); }; BlueImpMD5.prototype.raw_hmac_md5 = function (k, d) { return this.rstr_hmac_md5(this.str2rstr_utf8(k), this.str2rstr_utf8(d)); }; BlueImpMD5.prototype.hex_hmac_md5 = function (k, d) { return this.rstr2hex(this.raw_hmac_md5(k, d)); }; BlueImpMD5.prototype.md5 = function (string, key, raw) { if (!key) { if (!raw) { return this.hex_md5(string); } return this.raw_md5(string); } if (!raw) { return this.hex_hmac_md5(key, string); } return this.raw_hmac_md5(key, string); }; // CommonJS module { if ('object' !== 'undefined' && module.exports) { exports = module.exports = Chance; } exports.Chance = Chance; } // Register as an anonymous AMD module if (typeof undefined === 'function' && undefined.amd) { undefined([], function () { return Chance; }); } // if there is a importsScrips object define chance for worker // allows worker to use full Chance functionality with seed if (typeof importScripts !== 'undefined') { chance = new Chance(); self.Chance = Chance; } // If there is a window object, that at least has a document property, // instantiate and define chance on the window if (typeof window === "object" && typeof window.document === "object") { window.Chance = Chance; window.chance = new Chance(); } })(); }); /*! http://mths.be/fromcodepoint v0.2.1 by @mathias */ if (!String.fromCodePoint) { (function() { var defineProperty = (function() { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch(error) {} return result; }()); var stringFromCharCode = String.fromCharCode; var floor = Math.floor; var fromCodePoint = function(_) { var MAX_SIZE = 0x4000; var codeUnits = []; var highSurrogate; var lowSurrogate; var index = -1; var length = arguments.length; if (!length) { return ''; } var result = ''; while (++index < length) { var codePoint = Number(arguments[index]); if ( !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` codePoint < 0 || // not a valid Unicode code point codePoint > 0x10FFFF || // not a valid Unicode code point floor(codePoint) != codePoint // not an integer ) { throw RangeError('Invalid code point: ' + codePoint); } if (codePoint <= 0xFFFF) { // BMP code point codeUnits.push(codePoint); } else { // Astral code point; split in surrogate halves // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae codePoint -= 0x10000; highSurrogate = (codePoint >> 10) + 0xD800; lowSurrogate = (codePoint % 0x400) + 0xDC00; codeUnits.push(highSurrogate, lowSurrogate); } if (index + 1 == length || codeUnits.length > MAX_SIZE) { result += stringFromCharCode.apply(null, codeUnits); codeUnits.length = 0; } } return result; }; if (defineProperty) { defineProperty(String, 'fromCodePoint', { 'value': fromCodePoint, 'configurable': true, 'writable': true }); } else { String.fromCodePoint = fromCodePoint; } }()); } /*! http://mths.be/codepointat v0.2.0 by @mathias */ if (!String.prototype.codePointAt) { (function() { 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` var defineProperty = (function() { // IE 8 only supports `Object.defineProperty` on DOM elements try { var object = {}; var $defineProperty = Object.defineProperty; var result = $defineProperty(object, object, object) && $defineProperty; } catch(error) {} return result; }()); var codePointAt = function(position) { if (this == null) { throw TypeError(); } var string = String(this); var size = string.length; // `ToInteger` var index = position ? Number(position) : 0; if (index != index) { // better `isNaN` index = 0; } // Account for out-of-bounds indices: if (index < 0 || index >= size) { return undefined; } // Get the first code unit var first = string.charCodeAt(index); var second; if ( // check if it’s the start of a surrogate pair first >= 0xD800 && first <= 0xDBFF && // high surrogate size > index + 1 // there is a next code unit ) { second = string.charCodeAt(index + 1); if (second >= 0xDC00 && second <= 0xDFFF) { // low surrogate // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; } } return first; }; if (defineProperty) { defineProperty(String.prototype, 'codePointAt', { 'value': codePointAt, 'configurable': true, 'writable': true }); } else { String.prototype.codePointAt = codePointAt; } }()); } var UTF8_1 = createCommonjsModule$1(function (module) { // UTF8 : Manage UTF-8 strings in ArrayBuffers if(module.require) { } var UTF8={ // non UTF8 encoding detection (cf README file for details) 'isNotUTF8': function(bytes, byteOffset, byteLength) { try { UTF8.getStringFromBytes(bytes, byteOffset, byteLength, true); } catch(e) { return true; } return false; }, // UTF8 decoding functions 'getCharLength': function(theByte) { // 4 bytes encoded char (mask 11110000) if(0xF0 == (theByte&0xF0)) { return 4; // 3 bytes encoded char (mask 11100000) } else if(0xE0 == (theByte&0xE0)) { return 3; // 2 bytes encoded char (mask 11000000) } else if(0xC0 == (theByte&0xC0)) { return 2; // 1 bytes encoded char } else if(theByte == (theByte&0x7F)) { return 1; } return 0; }, 'getCharCode': function(bytes, byteOffset, charLength) { var charCode = 0, mask = ''; byteOffset = byteOffset || 0; // Retrieve charLength if not given charLength = charLength || UTF8.getCharLength(bytes[byteOffset]); if(charLength == 0) { throw new Error(bytes[byteOffset].toString(2)+' is not a significative' + ' byte (offset:'+byteOffset+').'); } // Return byte value if charlength is 1 if(1 === charLength) { return bytes[byteOffset]; } // Test UTF8 integrity mask = '00000000'.slice(0, charLength) + 1 + '00000000'.slice(charLength + 1); if(bytes[byteOffset]&(parseInt(mask, 2))) { throw Error('Index ' + byteOffset + ': A ' + charLength + ' bytes' + ' encoded char' +' cannot encode the '+(charLength+1)+'th rank bit to 1.'); } // Reading the first byte mask='0000'.slice(0,charLength+1)+'11111111'.slice(charLength+1); charCode+=(bytes[byteOffset]&parseInt(mask,2))<<((--charLength)*6); // Reading the next bytes while(charLength) { if(0x80!==(bytes[byteOffset+1]&0x80) ||0x40===(bytes[byteOffset+1]&0x40)) { throw Error('Index '+(byteOffset+1)+': Next bytes of encoded char' +' must begin with a "10" bit sequence.'); } charCode += ((bytes[++byteOffset]&0x3F) << ((--charLength) * 6)); } return charCode; }, 'getStringFromBytes': function(bytes, byteOffset, byteLength, strict) { var charLength, chars = []; byteOffset = byteOffset|0; byteLength=('number' === typeof byteLength ? byteLength : bytes.byteLength || bytes.length ); for(; byteOffset < byteLength; byteOffset++) { charLength = UTF8.getCharLength(bytes[byteOffset]); if(byteOffset + charLength > byteLength) { if(strict) { throw Error('Index ' + byteOffset + ': Found a ' + charLength + ' bytes encoded char declaration but only ' + (byteLength - byteOffset) +' bytes are available.'); } } else { chars.push(String.fromCodePoint( UTF8.getCharCode(bytes, byteOffset, charLength, strict) )); } byteOffset += charLength - 1; } return chars.join(''); }, // UTF8 encoding functions 'getBytesForCharCode': function(charCode) { if(charCode < 128) { return 1; } else if(charCode < 2048) { return 2; } else if(charCode < 65536) { return 3; } else if(charCode < 2097152) { return 4; } throw new Error('CharCode '+charCode+' cannot be encoded with UTF8.'); }, 'setBytesFromCharCode': function(charCode, bytes, byteOffset, neededBytes) { charCode = charCode|0; bytes = bytes || []; byteOffset = byteOffset|0; neededBytes = neededBytes || UTF8.getBytesForCharCode(charCode); // Setting the charCode as it to bytes if the byte length is 1 if(1 == neededBytes) { bytes[byteOffset] = charCode; } else { // Computing the first byte bytes[byteOffset++] = (parseInt('1111'.slice(0, neededBytes), 2) << 8 - neededBytes) + (charCode >>> ((--neededBytes) * 6)); // Computing next bytes for(;neededBytes>0;) { bytes[byteOffset++] = ((charCode>>>((--neededBytes) * 6))&0x3F)|0x80; } } return bytes; }, 'setBytesFromString': function(string, bytes, byteOffset, byteLength, strict) { string = string || ''; bytes = bytes || []; byteOffset = byteOffset|0; byteLength = ('number' === typeof byteLength ? byteLength : bytes.byteLength||Infinity ); for(var i = 0, j = string.length; i < j; i++) { var neededBytes = UTF8.getBytesForCharCode(string[i].codePointAt(0)); if(strict && byteOffset + neededBytes > byteLength) { throw new Error('Not enought bytes to encode the char "' + string[i] + '" at the offset "' + byteOffset + '".'); } UTF8.setBytesFromCharCode(string[i].codePointAt(0), bytes, byteOffset, neededBytes, strict); byteOffset += neededBytes; } return bytes; } }; { module.exports = UTF8; } }); const bits7 = 0b1111111; const bits8 = 0b11111111; class BinaryEncoder { constructor () { this.data = []; } get pos () { return this.data.length } createBuffer () { return Uint8Array.from(this.data).buffer } writeUint8 (num) { this.data.push(num & bits8); } setUint8 (pos, num) { this.data[pos] = num & bits8; } writeUint16 (num) { this.data.push(num & bits8, (num >>> 8) & bits8); } setUint16 (pos, num) { this.data[pos] = num & bits8; this.data[pos + 1] = (num >>> 8) & bits8; } writeUint32 (num) { for (let i = 0; i < 4; i++) { this.data.push(num & bits8); num >>>= 8; } } setUint32 (pos, num) { for (let i = 0; i < 4; i++) { this.data[pos + i] = num & bits8; num >>>= 8; } } writeVarUint (num) { while (num >= 0b10000000) { this.data.push(0b10000000 | (bits7 & num)); num >>>= 7; } this.data.push(bits7 & num); } writeVarString (str) { let bytes = UTF8_1.setBytesFromString(str); let len = bytes.length; this.writeVarUint(len); for (let i = 0; i < len; i++) { this.data.push(bytes[i]); } } writeOpID (id) { let user = id[0]; this.writeVarUint(user); if (user !== 0xFFFFFF) { this.writeVarUint(id[1]); } else { this.writeVarString(id[1]); } } } class BinaryDecoder { constructor (buffer) { this.uint8arr = new Uint8Array(buffer); this.pos = 0; } skip8 () { this.pos++; } readUint8 () { return this.uint8arr[this.pos++] } readUint32 () { let uint = this.uint8arr[this.pos] + (this.uint8arr[this.pos + 1] << 8) + (this.uint8arr[this.pos + 2] << 16) + (this.uint8arr[this.pos + 3] << 24); this.pos += 4; return uint } peekUint8 () { return this.uint8arr[this.pos] } readVarUint () { let num = 0; let len = 0; while (true) { let r = this.uint8arr[this.pos++]; num = num | ((r & bits7) << len); len += 7; if (r < 1 << 7) { return num >>> 0 // return unsigned number! } if (len > 35) { throw new Error('Integer out of range!') } } } readVarString () { let len = this.readVarUint(); let bytes = new Array(len); for (let i = 0; i < len; i++) { bytes[i] = this.uint8arr[this.pos++]; } return UTF8_1.getStringFromBytes(bytes) } peekVarString () { let pos = this.pos; let s = this.readVarString(); this.pos = pos; return s } readOpID () { let user = this.readVarUint(); if (user !== 0xFFFFFF) { return [user, this.readVarUint()] } else { return [user, this.readVarString()] } } } function formatYjsMessage (buffer) { let decoder = new BinaryDecoder(buffer); decoder.readVarString(); // read roomname let type = decoder.readVarString(); let strBuilder = []; strBuilder.push('\n === ' + type + ' ===\n'); if (type === 'update') { logMessageUpdate(decoder, strBuilder); } else if (type === 'sync step 1') { logMessageSyncStep1(decoder, strBuilder); } else if (type === 'sync step 2') { logMessageSyncStep2(decoder, strBuilder); } else { strBuilder.push('-- Unknown message type - probably an encoding issue!!!'); } return strBuilder.join('') } function formatYjsMessageType (buffer) { let decoder = new BinaryDecoder(buffer); decoder.readVarString(); // roomname return decoder.readVarString() } async function logMessageUpdate (decoder, strBuilder) { let len = decoder.readUint32(); for (let i = 0; i < len; i++) { strBuilder.push(JSON.stringify(Y.Struct.binaryDecodeOperation(decoder)) + '\n'); } } async function computeMessageUpdate (decoder, encoder, conn) { if (conn.y.db.forwardAppliedOperations) { let messagePosition = decoder.pos; let len = decoder.readUint32(); let delops = []; for (let i = 0; i < len; i++) { let op = Y.Struct.binaryDecodeOperation(decoder); if (op.struct === 'Delete') { delops.push(op); } } if (delops.length > 0) { conn.broadcastOps(delops); } decoder.pos = messagePosition; } conn.y.db.applyOperations(decoder); } function sendSyncStep1 (conn, syncUser) { conn.y.db.requestTransaction(function * () { let encoder = new BinaryEncoder(); encoder.writeVarString(conn.opts.room || ''); encoder.writeVarString('sync step 1'); encoder.writeVarString(conn.authInfo || ''); encoder.writeVarUint(conn.protocolVersion); let preferUntransformed = conn.preferUntransformed && this.os.length === 0; // TODO: length may not be defined encoder.writeUint8(preferUntransformed ? 1 : 0); yield * this.writeStateSet(encoder); conn.send(syncUser, encoder.createBuffer()); }); } function logMessageSyncStep1 (decoder, strBuilder) { let auth = decoder.readVarString(); let protocolVersion = decoder.readVarUint(); let preferUntransformed = decoder.readUint8() === 1; strBuilder.push(` - auth: "${auth}" - protocolVersion: ${protocolVersion} - preferUntransformed: ${preferUntransformed} `); logSS(decoder, strBuilder); } async function computeMessageSyncStep1 (decoder, encoder, conn, senderConn, sender) { let protocolVersion = decoder.readVarUint(); let preferUntransformed = decoder.readUint8() === 1; // check protocol version if (protocolVersion !== conn.protocolVersion) { console.warn( `You tried to sync with a yjs instance that has a different protocol version (You: ${protocolVersion}, Client: ${protocolVersion}). The sync was stopped. You need to upgrade your dependencies (especially Yjs & the Connector)! `); conn.y.destroy(); } // send sync step 2 conn.y.db.requestTransaction(function * () { encoder.writeVarString('sync step 2'); encoder.writeVarString(conn.authInfo || ''); let emptyStateSet = this.ds.length === 0; // TODO: length may not always be available if (preferUntransformed && emptyStateSet) { encoder.writeUint8(1); yield * this.writeOperationsUntransformed(encoder); } else { encoder.writeUint8(0); yield * this.writeOperations(encoder, decoder); } yield * this.writeDeleteSet(encoder); conn.send(senderConn.uid, encoder.createBuffer()); senderConn.receivedSyncStep2 = true; }); if (conn.role === 'slave') { sendSyncStep1(conn, sender); } await conn.y.db.whenTransactionsFinished(); } function logSS (decoder, strBuilder) { strBuilder.push(' == SS: \n'); let len = decoder.readUint32(); for (let i = 0; i < len; i++) { let user = decoder.readVarUint(); let clock = decoder.readVarUint(); strBuilder.push(` ${user}: ${clock}\n`); } } function logOS (decoder, strBuilder) { strBuilder.push(' == OS: \n'); let len = decoder.readUint32(); for (let i = 0; i < len; i++) { let op = Y.Struct.binaryDecodeOperation(decoder); strBuilder.push(JSON.stringify(op) + '\n'); } } function logDS (decoder, strBuilder) { strBuilder.push(' == DS: \n'); let len = decoder.readUint32(); for (let i = 0; i < len; i++) { let user = decoder.readVarUint(); strBuilder.push(` User: ${user}: `); let len2 = decoder.readVarUint(); for (let j = 0; j < len2; j++) { let from = decoder.readVarUint(); let to = decoder.readVarUint(); let gc = decoder.readUint8() === 1; strBuilder.push(`[${from}, ${to}, ${gc}]`); } } } function logMessageSyncStep2 (decoder, strBuilder) { strBuilder.push(' - auth: ' + decoder.readVarString() + '\n'); let osTransformed = decoder.readUint8() === 1; strBuilder.push(' - osUntransformed: ' + osTransformed + '\n'); logOS(decoder, strBuilder); if (osTransformed) { logSS(decoder, strBuilder); } logDS(decoder, strBuilder); } async function computeMessageSyncStep2 (decoder, encoder, conn, senderConn, sender) { var db = conn.y.db; let defer = senderConn.syncStep2; // apply operations first db.requestTransaction(function * () { let osUntransformed = decoder.readUint8(); if (osUntransformed === 1) { yield * this.applyOperationsUntransformed(decoder); } else { this.store.applyOperations(decoder); } }); // then apply ds await db.whenTransactionsFinished(); db.requestTransaction(function * () { yield * this.applyDeleteSet(decoder); }); await db.whenTransactionsFinished(); conn._setSyncedWith(sender); defer.resolve(); } function extendConnector (Y/* :any */) { class AbstractConnector { /* opts contains the following information: role : String Role of this client ("master" or "slave") */ constructor (y, opts) { this.y = y; if (opts == null) { opts = {}; } this.opts = opts; // Prefer to receive untransformed operations. This does only work if // this client receives operations from only one other client. // In particular, this does not work with y-webrtc. // It will work with y-websockets-client this.preferUntransformed = opts.preferUntransformed || false; if (opts.role == null || opts.role === 'master') { this.role = 'master'; } else if (opts.role === 'slave') { this.role = 'slave'; } else { throw new Error("Role must be either 'master' or 'slave'!") } this.log = Y.debug('y:connector'); this.logMessage = Y.debug('y:connector-message'); this.y.db.forwardAppliedOperations = opts.forwardAppliedOperations || false; this.role = opts.role; this.connections = new Map(); this.isSynced = false; this.userEventListeners = []; this.whenSyncedListeners = []; this.currentSyncTarget = null; this.debug = opts.debug === true; this.broadcastOpBuffer = []; this.protocolVersion = 11; this.authInfo = opts.auth || null; this.checkAuth = opts.checkAuth || function () { return Promise.resolve('write') }; // default is everyone has write access if (opts.generateUserId !== false) { this.setUserId(Y.utils.generateUserId()); } } reconnect () { this.log('reconnecting..'); return this.y.db.startGarbageCollector() } disconnect () { this.log('discronnecting..'); this.connections = new Map(); this.isSynced = false; this.currentSyncTarget = null; this.whenSyncedListeners = []; this.y.db.stopGarbageCollector(); return this.y.db.whenTransactionsFinished() } repair () { this.log('Repairing the state of Yjs. This can happen if messages get lost, and Yjs detects that something is wrong. If this happens often, please report an issue here: https://github.com/y-js/yjs/issues'); this.connections.forEach(user => { user.isSynced = false; }); this.isSynced = false; this.currentSyncTarget = null; this.findNextSyncTarget(); } setUserId (userId) { if (this.userId == null) { if (!Number.isInteger(userId)) { let err = new Error('UserId must be an integer!'); this.y.emit('error', err); throw err } this.log('Set userId to "%s"', userId); this.userId = userId; return this.y.db.setUserId(userId) } else { return null } } onUserEvent (f) { this.userEventListeners.push(f); } removeUserEventListener (f) { this.userEventListeners = this.userEventListeners.filter(g => f !== g); } userLeft (user) { if (this.connections.has(user)) { this.log('%s: User left %s', this.userId, user); this.connections.delete(user); if (user === this.currentSyncTarget) { this.currentSyncTarget = null; this.findNextSyncTarget(); } for (var f of this.userEventListeners) { f({ action: 'userLeft', user: user }); } } } userJoined (user, role) { if (role == null) { throw new Error('You must specify the role of the joined user!') } if (this.connections.has(user)) { throw new Error('This user already joined!') } this.log('%s: User joined %s', this.userId, user); this.connections.set(user, { uid: user, isSynced: false, role: role, processAfterAuth: [], receivedSyncStep2: false }); let defer = {}; defer.promise = new Promise(function (resolve) { defer.resolve = resolve; }); this.connections.get(user).syncStep2 = defer; for (var f of this.userEventListeners) { f({ action: 'userJoined', user: user, role: role }); } if (this.currentSyncTarget == null) { this.findNextSyncTarget(); } } // Execute a function _when_ we are connected. // If not connected, wait until connected whenSynced (f) { if (this.isSynced) { f(); } else { this.whenSyncedListeners.push(f); } } findNextSyncTarget () { if (this.currentSyncTarget != null || this.role === 'slave') { return // "The current sync has not finished or this is controlled by a master!" } var syncUser = null; for (var [uid, user] of this.connections) { if (!user.isSynced) { syncUser = uid; break } } var conn = this; if (syncUser != null) { this.currentSyncTarget = syncUser; sendSyncStep1(this, syncUser); } else { if (!conn.isSynced) { this.y.db.requestTransaction(function * () { if (!conn.isSynced) { // it is crucial that isSynced is set at the time garbageCollectAfterSync is called conn.isSynced = true; // It is safer to remove this! // TODO: remove: yield * this.garbageCollectAfterSync() // call whensynced listeners for (var f of conn.whenSyncedListeners) { f(); } conn.whenSyncedListeners = []; } }); } } } send (uid, buffer) { this.log('%s: Send \'%y\' to %s', this.userId, buffer, uid); this.logMessage('Message: %Y', buffer); } broadcast (buffer) { this.log('%s: Broadcast \'%y\'', this.userId, buffer); this.logMessage('Message: %Y', buffer); } /* Buffer operations, and broadcast them when ready. */ broadcastOps (ops) { ops = ops.map(function (op) { return Y.Struct[op.struct].encode(op) }); var self = this; function broadcastOperations () { if (self.broadcastOpBuffer.length > 0) { let encoder = new BinaryEncoder(); encoder.writeVarString(self.opts.room); encoder.writeVarString('update'); let ops = self.broadcastOpBuffer; self.broadcastOpBuffer = []; let length = ops.length; encoder.writeUint32(length); for (var i = 0; i < length; i++) { let op = ops[i]; Y.Struct[op.struct].binaryEncode(encoder, op); } self.broadcast(encoder.createBuffer()); } } if (this.broadcastOpBuffer.length === 0) { this.broadcastOpBuffer = ops; this.y.db.whenTransactionsFinished().then(broadcastOperations); } else { this.broadcastOpBuffer = this.broadcastOpBuffer.concat(ops); } } /* You received a raw message, and you know that it is intended for Yjs. Then call this function. */ async receiveMessage (sender, buffer) { if (sender === this.userId) { return } let decoder = new BinaryDecoder(buffer); let encoder = new BinaryEncoder(); let roomname = decoder.readVarString(); // read room name encoder.writeVarString(roomname); let messageType = decoder.readVarString(); let senderConn = this.connections.get(sender); this.log('%s: Receive \'%s\' from %s', this.userId, messageType, sender); this.logMessage('Message: %Y', buffer); if (senderConn == null) { throw new Error('Received message from unknown peer!') } if (messageType === 'sync step 1' || messageType === 'sync step 2') { let auth = decoder.readVarUint(); if (senderConn.auth == null) { // check auth let authPermissions = await this.checkAuth(auth, this.y, sender); senderConn.auth = authPermissions; this.y.emit('userAuthenticated', { user: senderConn.uid, auth: authPermissions }); senderConn.syncStep2.promise.then(() => { if (senderConn.processAfterAuth == null) { return } for (let i = 0; i < senderConn.processAfterAuth.length; i++) { let m = senderConn.processAfterAuth[i]; this.receiveMessage(m[0], m[1]); } senderConn.processAfterAuth = null; }); } } if (senderConn.auth == null) { senderConn.processAfterAuth.push([sender, buffer]); return } if (messageType === 'sync step 1' && (senderConn.auth === 'write' || senderConn.auth === 'read')) { // cannot wait for sync step 1 to finish, because we may wait for sync step 2 in sync step 1 (->lock) computeMessageSyncStep1(decoder, encoder, this, senderConn, sender); return this.y.db.whenTransactionsFinished() } else if (messageType === 'sync step 2' && senderConn.auth === 'write') { return computeMessageSyncStep2(decoder, encoder, this, senderConn, sender) } else if (messageType === 'update' && senderConn.auth === 'write') { return computeMessageUpdate(decoder, encoder, this, senderConn, sender) } else { console.error('Unable to receive message'); } } _setSyncedWith (user) { var conn = this.connections.get(user); if (conn != null) { conn.isSynced = true; } if (user === this.currentSyncTarget) { this.currentSyncTarget = null; this.findNextSyncTarget(); } } /* Currently, the HB encodes operations as JSON. For the moment I want to keep it that way. Maybe we support encoding in the HB as XML in the future, but for now I don't want too much overhead. Y is very likely to get changed a lot in the future Because we don't want to encode JSON as string (with character escaping, wich makes it pretty much unreadable) we encode the JSON as XML. When the HB support encoding as XML, the format should look pretty much like this. does not support primitive values as array elements expects an ltx (less than xml) object */ parseMessageFromXml (m/* :any */) { function parseArray (node) { for (var n of node.children) { if (n.getAttribute('isArray') === 'true') { return parseArray(n) } else { return parseObject(n) } } } function parseObject (node/* :any */) { var json = {}; for (var attrName in node.attrs) { var value = node.attrs[attrName]; var int = parseInt(value, 10); if (isNaN(int) || ('' + int) !== value) { json[attrName] = value; } else { json[attrName] = int; } } for (var n/* :any */ in node.children) { var name = n.name; if (n.getAttribute('isArray') === 'true') { json[name] = parseArray(n); } else { json[name] = parseObject(n); } } return json } parseObject(m); } /* encode message in xml we use string because Strophe only accepts an "xml-string".. So {a:4,b:{c:5}} will look like m - ltx element json - Object */ encodeMessageToXml (msg, obj) { // attributes is optional function encodeObject (m, json) { for (var name in json) { var value = json[name]; if (name == null) { // nop } else if (value.constructor === Object) { encodeObject(m.c(name), value); } else if (value.constructor === Array) { encodeArray(m.c(name), value); } else { m.setAttribute(name, value); } } } function encodeArray (m, array) { m.setAttribute('isArray', 'true'); for (var e of array) { if (e.constructor === Object) { encodeObject(m.c('array-element'), e); } else { encodeArray(m.c('array-element'), e); } } } if (obj.constructor === Object) { encodeObject(msg.c('y', { xmlns: 'http://y.ninja/connector-stanza' }), obj); } else if (obj.constructor === Array) { encodeArray(msg.c('y', { xmlns: 'http://y.ninja/connector-stanza' }), obj); } else { throw new Error("I can't encode this json!") } } } Y.AbstractConnector = AbstractConnector; } /* @flow */ function extendDatabase (Y /* :any */) { /* Partial definition of an OperationStore. TODO: name it Database, operation store only holds operations. A database definition must alse define the following methods: * logTable() (optional) - show relevant information information in a table * requestTransaction(makeGen) - request a transaction * destroy() - destroy the database */ class AbstractDatabase { /* :: y: YConfig; forwardAppliedOperations: boolean; listenersById: Object; listenersByIdExecuteNow: Array; listenersByIdRequestPending: boolean; initializedTypes: Object; whenUserIdSetListener: ?Function; waitingTransactions: Array; transactionInProgress: boolean; executeOrder: Array; gc1: Array; gc2: Array; gcTimeout: number; gcInterval: any; garbageCollect: Function; executeOrder: Array; // for debugging only userId: UserId; opClock: number; transactionsFinished: ?{promise: Promise, resolve: any}; transact: (x: ?Generator) => any; */ constructor (y, opts) { this.y = y; opts.gc = opts.gc === true; this.dbOpts = opts; var os = this; this.userId = null; var resolve_; this.userIdPromise = new Promise(function (resolve) { resolve_ = resolve; }); this.userIdPromise.resolve = resolve_; // whether to broadcast all applied operations (insert & delete hook) this.forwardAppliedOperations = false; // E.g. this.listenersById[id] : Array this.listenersById = {}; // Execute the next time a transaction is requested this.listenersByIdExecuteNow = []; // A transaction is requested this.listenersByIdRequestPending = false; /* To make things more clear, the following naming conventions: * ls : we put this.listenersById on ls * l : Array * id : Id (can't use as property name) * sid : String (converted from id via JSON.stringify so we can use it as a property name) Always remember to first overwrite a property before you iterate over it! */ // TODO: Use ES7 Weak Maps. This way types that are no longer user, // wont be kept in memory. this.initializedTypes = {}; this.waitingTransactions = []; this.transactionInProgress = false; this.transactionIsFlushed = false; if (typeof YConcurrencyTestingMode !== 'undefined') { this.executeOrder = []; } this.gc1 = []; // first stage this.gc2 = []; // second stage -> after that, remove the op function garbageCollect () { return os.whenTransactionsFinished().then(function () { if (os.gcTimeout > 0 && (os.gc1.length > 0 || os.gc2.length > 0)) { if (!os.y.connector.isSynced) { console.warn('gc should be empty when not synced!'); } return new Promise((resolve) => { os.requestTransaction(function * () { if (os.y.connector != null && os.y.connector.isSynced) { for (var i = 0; i < os.gc2.length; i++) { var oid = os.gc2[i]; yield * this.garbageCollectOperation(oid); } os.gc2 = os.gc1; os.gc1 = []; } // TODO: Use setInterval here instead (when garbageCollect is called several times there will be several timeouts..) if (os.gcTimeout > 0) { os.gcInterval = setTimeout(garbageCollect, os.gcTimeout); } resolve(); }); }) } else { // TODO: see above if (os.gcTimeout > 0) { os.gcInterval = setTimeout(garbageCollect, os.gcTimeout); } return Promise.resolve() } }) } this.garbageCollect = garbageCollect; this.startGarbageCollector(); this.repairCheckInterval = !opts.repairCheckInterval ? 6000 : opts.repairCheckInterval; this.opsReceivedTimestamp = new Date(); this.startRepairCheck(); } startGarbageCollector () { this.gc = this.dbOpts.gc; if (this.gc) { this.gcTimeout = !this.dbOpts.gcTimeout ? 100000 : this.dbOpts.gcTimeout; } else { this.gcTimeout = -1; } if (this.gcTimeout > 0) { this.garbageCollect(); } } startRepairCheck () { var os = this; if (this.repairCheckInterval > 0) { this.repairCheckIntervalHandler = setInterval(function repairOnMissingOperations () { /* Case 1. No ops have been received in a while (new Date() - os.opsReceivedTimestamp > os.repairCheckInterval) - 1.1 os.listenersById is empty. Then the state was correct the whole time. -> Nothing to do (nor to update) - 1.2 os.listenersById is not empty. * Then the state was incorrect for at least {os.repairCheckInterval} seconds. * -> Remove everything in os.listenersById and sync again (connector.repair()) Case 2. An op has been received in the last {os.repairCheckInterval } seconds. It is not yet necessary to check for faulty behavior. Everything can still resolve itself. Wait for more messages. If nothing was received for a while and os.listenersById is still not emty, we are in case 1.2 -> Do nothing Baseline here is: we really only have to catch case 1.2.. */ if ( new Date() - os.opsReceivedTimestamp > os.repairCheckInterval && Object.keys(os.listenersById).length > 0 // os.listenersById is not empty ) { // haven't received operations for over {os.repairCheckInterval} seconds, resend state vector os.listenersById = {}; os.opsReceivedTimestamp = new Date(); // update so you don't send repair several times in a row os.y.connector.repair(); } }, this.repairCheckInterval); } } stopRepairCheck () { clearInterval(this.repairCheckIntervalHandler); } queueGarbageCollector (id) { if (this.y.connector.isSynced && this.gc) { this.gc1.push(id); } } emptyGarbageCollector () { return new Promise(resolve => { var check = () => { if (this.gc1.length > 0 || this.gc2.length > 0) { this.garbageCollect().then(check); } else { resolve(); } }; setTimeout(check, 0); }) } addToDebug () { if (typeof YConcurrencyTestingMode !== 'undefined') { var command /* :string */ = Array.prototype.map.call(arguments, function (s) { if (typeof s === 'string') { return s } else { return JSON.stringify(s) } }).join('').replace(/"/g, "'").replace(/,/g, ', ').replace(/:/g, ': '); this.executeOrder.push(command); } } getDebugData () { console.log(this.executeOrder.join('\n')); } stopGarbageCollector () { var self = this; this.gc = false; this.gcTimeout = -1; return new Promise(function (resolve) { self.requestTransaction(function * () { var ungc /* :Array */ = self.gc1.concat(self.gc2); self.gc1 = []; self.gc2 = []; for (var i = 0; i < ungc.length; i++) { var op = yield * this.getOperation(ungc[i]); if (op != null) { delete op.gc; yield * this.setOperation(op); } } resolve(); }); }) } /* Try to add to GC. TODO: rename this function Rulez: * Only gc if this user is online & gc turned on * The most left element in a list must not be gc'd. => There is at least one element in the list returns true iff op was added to GC */ * addToGarbageCollector (op, left) { if ( op.gc == null && op.deleted === true && this.store.gc && this.store.y.connector.isSynced ) { var gc = false; if (left != null && left.deleted === true) { gc = true; } else if (op.content != null && op.content.length > 1) { op = yield * this.getInsertionCleanStart([op.id[0], op.id[1] + 1]); gc = true; } if (gc) { op.gc = true; yield * this.setOperation(op); this.store.queueGarbageCollector(op.id); return true } } return false } removeFromGarbageCollector (op) { function filter (o) { return !Y.utils.compareIds(o, op.id) } this.gc1 = this.gc1.filter(filter); this.gc2 = this.gc2.filter(filter); delete op.gc; } destroyTypes () { for (var key in this.initializedTypes) { var type = this.initializedTypes[key]; if (type._destroy != null) { type._destroy(); } else { console.error('The type you included does not provide destroy functionality, it will remain in memory (updating your packages will help).'); } } } * destroy () { clearTimeout(this.gcInterval); this.gcInterval = null; this.stopRepairCheck(); } setUserId (userId) { if (!this.userIdPromise.inProgress) { this.userIdPromise.inProgress = true; var self = this; self.requestTransaction(function * () { self.userId = userId; var state = yield * this.getState(userId); self.opClock = state.clock; self.userIdPromise.resolve(userId); }); } return this.userIdPromise } whenUserIdSet (f) { this.userIdPromise.then(f); } getNextOpId (numberOfIds) { if (numberOfIds == null) { throw new Error('getNextOpId expects the number of created ids to create!') } else if (this.userId == null) { throw new Error('OperationStore not yet initialized!') } else { var id = [this.userId, this.opClock]; this.opClock += numberOfIds; return id } } /* Apply a list of operations. * we save a timestamp, because we received new operations that could resolve ops in this.listenersById (see this.startRepairCheck) * get a transaction * check whether all Struct.*.requiredOps are in the OS * check if it is an expected op (otherwise wait for it) * check if was deleted, apply a delete operation after op was applied */ applyOperations (decoder) { this.opsReceivedTimestamp = new Date(); let length = decoder.readUint32(); for (var i = 0; i < length; i++) { let o = Y.Struct.binaryDecodeOperation(decoder); if (o.id == null || o.id[0] !== this.y.connector.userId) { var required = Y.Struct[o.struct].requiredOps(o); if (o.requires != null) { required = required.concat(o.requires); } this.whenOperationsExist(required, o); } } } /* op is executed as soon as every operation requested is available. Note that Transaction can (and should) buffer requests. */ whenOperationsExist (ids, op) { if (ids.length > 0) { let listener = { op: op, missing: ids.length }; for (let i = 0; i < ids.length; i++) { let id = ids[i]; let sid = JSON.stringify(id); let l = this.listenersById[sid]; if (l == null) { l = []; this.listenersById[sid] = l; } l.push(listener); } } else { this.listenersByIdExecuteNow.push({ op: op }); } if (this.listenersByIdRequestPending) { return } this.listenersByIdRequestPending = true; var store = this; this.requestTransaction(function * () { var exeNow = store.listenersByIdExecuteNow; store.listenersByIdExecuteNow = []; var ls = store.listenersById; store.listenersById = {}; store.listenersByIdRequestPending = false; for (let key = 0; key < exeNow.length; key++) { let o = exeNow[key].op; yield * store.tryExecute.call(this, o); } for (var sid in ls) { var l = ls[sid]; var id = JSON.parse(sid); var op; if (typeof id[1] === 'string') { op = yield * this.getOperation(id); } else { op = yield * this.getInsertion(id); } if (op == null) { store.listenersById[sid] = l; } else { for (let i = 0; i < l.length; i++) { let listener = l[i]; let o = listener.op; if (--listener.missing === 0) { yield * store.tryExecute.call(this, o); } } } } }); } /* Actually execute an operation, when all expected operations are available. */ /* :: // TODO: this belongs somehow to transaction store: Object; getOperation: any; isGarbageCollected: any; addOperation: any; whenOperationsExist: any; */ * tryExecute (op) { this.store.addToDebug('yield * this.store.tryExecute.call(this, ', JSON.stringify(op), ')'); if (op.struct === 'Delete') { yield * Y.Struct.Delete.execute.call(this, op); // this is now called in Transaction.deleteOperation! // yield * this.store.operationAdded(this, op) } else { // check if this op was defined var defined = yield * this.getInsertion(op.id); while (defined != null && defined.content != null) { // check if this op has a longer content in the case it is defined if (defined.id[1] + defined.content.length < op.id[1] + op.content.length) { var overlapSize = defined.content.length - (op.id[1] - defined.id[1]); op.content.splice(0, overlapSize); op.id = [op.id[0], op.id[1] + overlapSize]; op.left = Y.utils.getLastId(defined); op.origin = op.left; defined = yield * this.getOperation(op.id); // getOperation suffices here } else { break } } if (defined == null) { var opid = op.id; var isGarbageCollected = yield * this.isGarbageCollected(opid); if (!isGarbageCollected) { // TODO: reduce number of get / put calls for op .. yield * Y.Struct[op.struct].execute.call(this, op); yield * this.addOperation(op); yield * this.store.operationAdded(this, op); // operationAdded can change op.. op = yield * this.getOperation(opid); // if insertion, try to combine with left yield * this.tryCombineWithLeft(op); } } } } /* * Called by a transaction when an operation is added. * This function is especially important for y-indexeddb, where several instances may share a single database. * Every time an operation is created by one instance, it is send to all other instances and operationAdded is called * * If it's not a Delete operation: * * Checks if another operation is executable (listenersById) * * Update state, if possible * * Always: * * Call type */ * operationAdded (transaction, op) { if (op.struct === 'Delete') { var type = this.initializedTypes[JSON.stringify(op.targetParent)]; if (type != null) { yield * type._changed(transaction, op); } } else { // increase SS yield * transaction.updateState(op.id[0]); var opLen = op.content != null ? op.content.length : 1; for (let i = 0; i < opLen; i++) { // notify whenOperation listeners (by id) var sid = JSON.stringify([op.id[0], op.id[1] + i]); var l = this.listenersById[sid]; delete this.listenersById[sid]; if (l != null) { for (var key in l) { var listener = l[key]; if (--listener.missing === 0) { this.whenOperationsExist([], listener.op); } } } } var t = this.initializedTypes[JSON.stringify(op.parent)]; // if parent is deleted, mark as gc'd and return if (op.parent != null) { var parentIsDeleted = yield * transaction.isDeleted(op.parent); if (parentIsDeleted) { yield * transaction.deleteList(op.id); return } } // notify parent, if it was instanciated as a custom type if (t != null) { let o = Y.utils.copyOperation(op); yield * t._changed(transaction, o); } if (!op.deleted) { // Delete if DS says this is actually deleted var len = op.content != null ? op.content.length : 1; var startId = op.id; // You must not use op.id in the following loop, because op will change when deleted // TODO: !! console.log('TODO: change this before commiting') for (let i = 0; i < len; i++) { var id = [startId[0], startId[1] + i]; var opIsDeleted = yield * transaction.isDeleted(id); if (opIsDeleted) { var delop = { struct: 'Delete', target: id }; yield * this.tryExecute.call(transaction, delop); } } } } } whenTransactionsFinished () { if (this.transactionInProgress) { if (this.transactionsFinished == null) { var resolve_; var promise = new Promise(function (resolve) { resolve_ = resolve; }); this.transactionsFinished = { resolve: resolve_, promise: promise }; } return this.transactionsFinished.promise } else { return Promise.resolve() } } // Check if there is another transaction request. // * the last transaction is always a flush :) getNextRequest () { if (this.waitingTransactions.length === 0) { if (this.transactionIsFlushed) { this.transactionInProgress = false; this.transactionIsFlushed = false; if (this.transactionsFinished != null) { this.transactionsFinished.resolve(); this.transactionsFinished = null; } return null } else { this.transactionIsFlushed = true; return function * () { yield * this.flush(); } } } else { this.transactionIsFlushed = false; return this.waitingTransactions.shift() } } requestTransaction (makeGen/* :any */, callImmediately) { this.waitingTransactions.push(makeGen); if (!this.transactionInProgress) { this.transactionInProgress = true; setTimeout(() => { this.transact(this.getNextRequest()); }, 0); } } /* Get a created/initialized type. */ getType (id) { return this.initializedTypes[JSON.stringify(id)] } /* Init type. This is called when a remote operation is retrieved, and transformed to a type TODO: delete type from store.initializedTypes[id] when corresponding id was deleted! */ * initType (id, args) { var sid = JSON.stringify(id); var t = this.store.initializedTypes[sid]; if (t == null) { var op/* :MapStruct | ListStruct */ = yield * this.getOperation(id); if (op != null) { t = yield * Y[op.type].typeDefinition.initType.call(this, this.store, op, args); this.store.initializedTypes[sid] = t; } } return t } /* Create type. This is called when the local user creates a type (which is a synchronous action) */ createType (typedefinition, id) { var structname = typedefinition[0].struct; id = id || this.getNextOpId(1); var op = Y.Struct[structname].create(id); op.type = typedefinition[0].name; this.requestTransaction(function * () { if (op.id[0] === 0xFFFFFF) { yield * this.setOperation(op); } else { yield * this.applyCreatedOperations([op]); } }); var t = Y[op.type].typeDefinition.createType(this, op, typedefinition[1]); this.initializedTypes[JSON.stringify(op.id)] = t; return t } } Y.AbstractDatabase = AbstractDatabase; } /* Partial definition of a transaction A transaction provides all the the async functionality on a database. By convention, a transaction has the following properties: * ss for StateSet * os for OperationStore * ds for DeleteStore A transaction must also define the following methods: * checkDeleteStoreForState(state) - When increasing the state of a user, an operation with an higher id may already be garbage collected, and therefore it will never be received. update the state to reflect this knowledge. This won't call a method to save the state! * getDeleteSet(id) - Get the delete set in a readable format: { "userX": [ [5,1], // starting from position 5, one operations is deleted [9,4] // starting from position 9, four operations are deleted ], "userY": ... } * getOpsFromDeleteSet(ds) -- TODO: just call this.deleteOperation(id) here - get a set of deletions that need to be applied in order to get to achieve the state of the supplied ds * setOperation(op) - write `op` to the database. Note: this is allowed to return an in-memory object. E.g. the Memory adapter returns the object that it has in-memory. Changing values on this object will be stored directly in the database without calling this function. Therefore, setOperation may have no functionality in some adapters. This also has implications on the way we use operations that were served from the database. We try not to call copyObject, if not necessary. * addOperation(op) - add an operation to the database. This may only be called once for every op.id Must return a function that returns the next operation in the database (ordered by id) * getOperation(id) * removeOperation(id) - remove an operation from the database. This is called when an operation is garbage collected. * setState(state) - `state` is of the form { user: "1", clock: 4 } <- meaning that we have four operations from user "1" (with these id's respectively: 0, 1, 2, and 3) * getState(user) * getStateVector() - Get the state of the OS in the form [{ user: "userX", clock: 11 }, .. ] * getStateSet() - Get the state of the OS in the form { "userX": 11, "userY": 22 } * getOperations(startSS) - Get the all the operations that are necessary in order to achive the stateSet of this user, starting from a stateSet supplied by another user * makeOperationReady(ss, op) - this is called only by `getOperations(startSS)`. It makes an operation applyable on a given SS. */ function extendTransaction (Y) { class TransactionInterface { /* :: store: Y.AbstractDatabase; ds: Store; os: Store; ss: Store; */ /* Apply operations that this user created (no remote ones!) * does not check for Struct.*.requiredOps() * also broadcasts it through the connector */ * applyCreatedOperations (ops) { var send = []; for (var i = 0; i < ops.length; i++) { var op = ops[i]; yield * this.store.tryExecute.call(this, op); if (op.id == null || typeof op.id[1] !== 'string') { send.push(Y.Struct[op.struct].encode(op)); } } if (send.length > 0) { // TODO: && !this.store.forwardAppliedOperations (but then i don't send delete ops) // is connected, and this is not going to be send in addOperation this.store.y.connector.broadcastOps(send); } } * deleteList (start) { while (start != null) { start = yield * this.getOperation(start); if (!start.gc) { start.gc = true; start.deleted = true; yield * this.setOperation(start); var delLength = start.content != null ? start.content.length : 1; yield * this.markDeleted(start.id, delLength); if (start.opContent != null) { yield * this.deleteOperation(start.opContent); } this.store.queueGarbageCollector(start.id); } start = start.right; } } /* Mark an operation as deleted, and add it to the GC, if possible. */ * deleteOperation (targetId, length, preventCallType) /* :Generator */ { if (length == null) { length = 1; } yield * this.markDeleted(targetId, length); while (length > 0) { var callType = false; var target = yield * this.os.findWithUpperBound([targetId[0], targetId[1] + length - 1]); var targetLength = target != null && target.content != null ? target.content.length : 1; if (target == null || target.id[0] !== targetId[0] || target.id[1] + targetLength <= targetId[1]) { // does not exist or is not in the range of the deletion target = null; length = 0; } else { // does exist, check if it is too long if (!target.deleted) { if (target.id[1] < targetId[1]) { // starts to the left of the deletion range target = yield * this.getInsertionCleanStart(targetId); targetLength = target.content.length; // must have content property! } if (target.id[1] + targetLength > targetId[1] + length) { // ends to the right of the deletion range target = yield * this.getInsertionCleanEnd([targetId[0], targetId[1] + length - 1]); targetLength = target.content.length; } } length = target.id[1] - targetId[1]; } if (target != null) { if (!target.deleted) { callType = true; // set deleted & notify type target.deleted = true; // delete containing lists if (target.start != null) { // TODO: don't do it like this .. -.- yield * this.deleteList(target.start); // yield * this.deleteList(target.id) -- do not gc itself because this may still get referenced } if (target.map != null) { for (var name in target.map) { yield * this.deleteList(target.map[name]); } // TODO: here to.. (see above) // yield * this.deleteList(target.id) -- see above } if (target.opContent != null) { yield * this.deleteOperation(target.opContent); // target.opContent = null } if (target.requires != null) { for (var i = 0; i < target.requires.length; i++) { yield * this.deleteOperation(target.requires[i]); } } } var left; if (target.left != null) { left = yield * this.getInsertion(target.left); } else { left = null; } // set here because it was deleted and/or gc'd yield * this.setOperation(target); /* Check if it is possible to add right to the gc. Because this delete can't be responsible for left being gc'd, we don't have to add left to the gc.. */ var right; if (target.right != null) { right = yield * this.getOperation(target.right); } else { right = null; } if (callType && !preventCallType) { yield * this.store.operationAdded(this, { struct: 'Delete', target: target.id, length: targetLength, targetParent: target.parent }); } // need to gc in the end! yield * this.store.addToGarbageCollector.call(this, target, left); if (right != null) { yield * this.store.addToGarbageCollector.call(this, right, target); } } } } /* Mark an operation as deleted&gc'd */ * markGarbageCollected (id, len) { // this.mem.push(["gc", id]); this.store.addToDebug('yield * this.markGarbageCollected(', id, ', ', len, ')'); var n = yield * this.markDeleted(id, len); if (n.id[1] < id[1] && !n.gc) { // un-extend left var newlen = n.len - (id[1] - n.id[1]); n.len -= newlen; yield * this.ds.put(n); n = {id: id, len: newlen, gc: false}; yield * this.ds.put(n); } // get prev&next before adding a new operation var prev = yield * this.ds.findPrev(id); var next = yield * this.ds.findNext(id); if (id[1] + len < n.id[1] + n.len && !n.gc) { // un-extend right yield * this.ds.put({id: [id[0], id[1] + len], len: n.len - len, gc: false}); n.len = len; } // set gc'd n.gc = true; // can extend left? if ( prev != null && prev.gc && Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id) ) { prev.len += n.len; yield * this.ds.delete(n.id); n = prev; // ds.put n here? } // can extend right? if ( next != null && next.gc && Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id) ) { n.len += next.len; yield * this.ds.delete(next.id); } yield * this.ds.put(n); yield * this.updateState(n.id[0]); } /* Mark an operation as deleted. returns the delete node */ * markDeleted (id, length) { if (length == null) { length = 1; } // this.mem.push(["del", id]); var n = yield * this.ds.findWithUpperBound(id); if (n != null && n.id[0] === id[0]) { if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) { // id is in n's range var diff = id[1] + length - (n.id[1] + n.len); // overlapping right if (diff > 0) { // id+length overlaps n if (!n.gc) { n.len += diff; } else { diff = n.id[1] + n.len - id[1]; // overlapping left (id till n.end) if (diff < length) { // a partial deletion n = {id: [id[0], id[1] + diff], len: length - diff, gc: false}; yield * this.ds.put(n); } else { // already gc'd throw new Error( 'DS reached an inconsistent state. Please report this issue!' ) } } } else { // no overlapping, already deleted return n } } else { // cannot extend left (there is no left!) n = {id: id, len: length, gc: false}; yield * this.ds.put(n); // TODO: you double-put !! } } else { // cannot extend left n = {id: id, len: length, gc: false}; yield * this.ds.put(n); } // can extend right? var next = yield * this.ds.findNext(n.id); if ( next != null && n.id[0] === next.id[0] && n.id[1] + n.len >= next.id[1] ) { diff = n.id[1] + n.len - next.id[1]; // from next.start to n.end while (diff >= 0) { // n overlaps with next if (next.gc) { // gc is stronger, so reduce length of n n.len -= diff; if (diff >= next.len) { // delete the missing range after next diff = diff - next.len; // missing range after next if (diff > 0) { yield * this.ds.put(n); // unneccessary? TODO! yield * this.markDeleted([next.id[0], next.id[1] + next.len], diff); } } break } else { // we can extend n with next if (diff > next.len) { // n is even longer than next // get next.next, and try to extend it var _next = yield * this.ds.findNext(next.id); yield * this.ds.delete(next.id); if (_next == null || n.id[0] !== _next.id[0]) { break } else { next = _next; diff = n.id[1] + n.len - next.id[1]; // from next.start to n.end // continue! } } else { // n just partially overlaps with next. extend n, delete next, and break this loop n.len += next.len - diff; yield * this.ds.delete(next.id); break } } } } yield * this.ds.put(n); return n } /* Call this method when the client is connected&synced with the other clients (e.g. master). This will query the database for operations that can be gc'd and add them to the garbage collector. */ * garbageCollectAfterSync () { // debugger if (this.store.gc1.length > 0 || this.store.gc2.length > 0) { console.warn('gc should be empty after sync'); } if (!this.store.gc) { return } yield * this.os.iterate(this, null, null, function * (op) { if (op.gc) { delete op.gc; yield * this.setOperation(op); } if (op.parent != null) { var parentDeleted = yield * this.isDeleted(op.parent); if (parentDeleted) { op.gc = true; if (!op.deleted) { yield * this.markDeleted(op.id, op.content != null ? op.content.length : 1); op.deleted = true; if (op.opContent != null) { yield * this.deleteOperation(op.opContent); } if (op.requires != null) { for (var i = 0; i < op.requires.length; i++) { yield * this.deleteOperation(op.requires[i]); } } } yield * this.setOperation(op); this.store.gc1.push(op.id); // this is ok becaues its shortly before sync (otherwise use queueGarbageCollector!) return } } if (op.deleted) { var left = null; if (op.left != null) { left = yield * this.getInsertion(op.left); } yield * this.store.addToGarbageCollector.call(this, op, left); } }); } /* Really remove an op and all its effects. The complicated case here is the Insert operation: * reset left * reset right * reset parent.start * reset parent.end * reset origins of all right ops */ * garbageCollectOperation (id) { this.store.addToDebug('yield * this.garbageCollectOperation(', id, ')'); var o = yield * this.getOperation(id); yield * this.markGarbageCollected(id, (o != null && o.content != null) ? o.content.length : 1); // always mark gc'd // if op exists, then clean that mess up.. if (o != null) { var deps = []; if (o.opContent != null) { deps.push(o.opContent); } if (o.requires != null) { deps = deps.concat(o.requires); } for (var i = 0; i < deps.length; i++) { var dep = yield * this.getOperation(deps[i]); if (dep != null) { if (!dep.deleted) { yield * this.deleteOperation(dep.id); dep = yield * this.getOperation(dep.id); } dep.gc = true; yield * this.setOperation(dep); this.store.queueGarbageCollector(dep.id); } else { yield * this.markGarbageCollected(deps[i], 1); } } // remove gc'd op from the left op, if it exists if (o.left != null) { var left = yield * this.getInsertion(o.left); left.right = o.right; yield * this.setOperation(left); } // remove gc'd op from the right op, if it exists // also reset origins of right ops if (o.right != null) { var right = yield * this.getOperation(o.right); right.left = o.left; yield * this.setOperation(right); if (o.originOf != null && o.originOf.length > 0) { // find new origin of right ops // origin is the first left operation var neworigin = o.left; // reset origin of all right ops (except first right - duh!), /* ** The following code does not rely on the the originOf property ** I recently added originOf to all Insert Operations (see Struct.Insert.execute), which saves which operations originate in a Insert operation. Garbage collecting without originOf is more memory efficient, but is nearly impossible for large texts, or lists! But I keep this code for now ``` // reset origin of right right.origin = neworigin // search until you find origin pointer to the left of o if (right.right != null) { var i = yield * this.getOperation(right.right) var ids = [o.id, o.right] while (ids.some(function (id) { return Y.utils.compareIds(id, i.origin) })) { if (Y.utils.compareIds(i.origin, o.id)) { // reset origin of i i.origin = neworigin yield * this.setOperation(i) } // get next i if (i.right == null) { break } else { ids.push(i.id) i = yield * this.getOperation(i.right) } } } ``` */ // ** Now the new implementation starts ** // reset neworigin of all originOf[*] for (var _i in o.originOf) { var originsIn = yield * this.getOperation(o.originOf[_i]); if (originsIn != null) { originsIn.origin = neworigin; yield * this.setOperation(originsIn); } } if (neworigin != null) { var neworigin_ = yield * this.getInsertion(neworigin); if (neworigin_.originOf == null) { neworigin_.originOf = o.originOf; } else { neworigin_.originOf = o.originOf.concat(neworigin_.originOf); } yield * this.setOperation(neworigin_); } // we don't need to set right here, because // right should be in o.originOf => it is set it the previous for loop } } // o may originate in another operation. // Since o is deleted, we have to reset o.origin's `originOf` property if (o.origin != null) { var origin = yield * this.getInsertion(o.origin); origin.originOf = origin.originOf.filter(function (_id) { return !Y.utils.compareIds(id, _id) }); yield * this.setOperation(origin); } var parent; if (o.parent != null) { parent = yield * this.getOperation(o.parent); } // remove gc'd op from parent, if it exists if (parent != null) { var setParent = false; // whether to save parent to the os if (o.parentSub != null) { if (Y.utils.compareIds(parent.map[o.parentSub], o.id)) { setParent = true; if (o.right != null) { parent.map[o.parentSub] = o.right; } else { delete parent.map[o.parentSub]; } } } else { if (Y.utils.compareIds(parent.start, o.id)) { // gc'd op is the start setParent = true; parent.start = o.right; } if (Y.utils.matchesId(o, parent.end)) { // gc'd op is the end setParent = true; parent.end = o.left; } } if (setParent) { yield * this.setOperation(parent); } } // finally remove it from the os yield * this.removeOperation(o.id); } } * checkDeleteStoreForState (state) { var n = yield * this.ds.findWithUpperBound([state.user, state.clock]); if (n != null && n.id[0] === state.user && n.gc) { state.clock = Math.max(state.clock, n.id[1] + n.len); } } * updateState (user) { var state = yield * this.getState(user); yield * this.checkDeleteStoreForState(state); var o = yield * this.getInsertion([user, state.clock]); var oLength = (o != null && o.content != null) ? o.content.length : 1; while (o != null && user === o.id[0] && o.id[1] <= state.clock && o.id[1] + oLength > state.clock) { // either its a new operation (1. case), or it is an operation that was deleted, but is not yet in the OS state.clock += oLength; yield * this.checkDeleteStoreForState(state); o = yield * this.os.findNext(o.id); oLength = (o != null && o.content != null) ? o.content.length : 1; } yield * this.setState(state); } /* apply a delete set in order to get the state of the supplied ds */ * applyDeleteSet (decoder) { var deletions = []; let dsLength = decoder.readUint32(); for (let i = 0; i < dsLength; i++) { let user = decoder.readVarUint(); let dv = []; let dvLength = decoder.readVarUint(); for (let j = 0; j < dvLength; j++) { let from = decoder.readVarUint(); let len = decoder.readVarUint(); let gc = decoder.readUint8() === 1; dv.push([from, len, gc]); } var pos = 0; var d = dv[pos]; yield * this.ds.iterate(this, [user, 0], [user, Number.MAX_VALUE], function * (n) { // cases: // 1. d deletes something to the right of n // => go to next n (break) // 2. d deletes something to the left of n // => create deletions // => reset d accordingly // *)=> if d doesn't delete anything anymore, go to next d (continue) // 3. not 2) and d deletes something that also n deletes // => reset d so that it doesn't contain n's deletion // *)=> if d does not delete anything anymore, go to next d (continue) while (d != null) { var diff = 0; // describe the diff of length in 1) and 2) if (n.id[1] + n.len <= d[0]) { // 1) break } else if (d[0] < n.id[1]) { // 2) // delete maximum the len of d // else delete as much as possible diff = Math.min(n.id[1] - d[0], d[1]); deletions.push([user, d[0], diff, d[2]]); } else { // 3) diff = n.id[1] + n.len - d[0]; // never null (see 1) if (d[2] && !n.gc) { // d marks as gc'd but n does not // then delete either way deletions.push([user, d[0], Math.min(diff, d[1]), d[2]]); } } if (d[1] <= diff) { // d doesn't delete anything anymore d = dv[++pos]; } else { d[0] = d[0] + diff; // reset pos d[1] = d[1] - diff; // reset length } } }); // for the rest.. just apply it for (; pos < dv.length; pos++) { d = dv[pos]; deletions.push([user, d[0], d[1], d[2]]); } } for (var i = 0; i < deletions.length; i++) { var del = deletions[i]; // always try to delete.. yield * this.deleteOperation([del[0], del[1]], del[2]); if (del[3]) { // gc.. yield * this.markGarbageCollected([del[0], del[1]], del[2]); // always mark gc'd // remove operation.. var counter = del[1] + del[2]; while (counter >= del[1]) { var o = yield * this.os.findWithUpperBound([del[0], counter - 1]); if (o == null) { break } var oLen = o.content != null ? o.content.length : 1; if (o.id[0] !== del[0] || o.id[1] + oLen <= del[1]) { // not in range break } if (o.id[1] + oLen > del[1] + del[2]) { // overlaps right o = yield * this.getInsertionCleanEnd([del[0], del[1] + del[2] - 1]); } if (o.id[1] < del[1]) { // overlaps left o = yield * this.getInsertionCleanStart([del[0], del[1]]); } counter = o.id[1]; yield * this.garbageCollectOperation(o.id); } } if (this.store.forwardAppliedOperations) { var ops = []; ops.push({struct: 'Delete', target: [del[0], del[1]], length: del[2]}); this.store.y.connector.broadcastOps(ops); } } } * isGarbageCollected (id) { var n = yield * this.ds.findWithUpperBound(id); return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len && n.gc } /* A DeleteSet (ds) describes all the deleted ops in the OS */ * writeDeleteSet (encoder) { var ds = new Map(); yield * this.ds.iterate(this, null, null, function * (n) { var user = n.id[0]; var counter = n.id[1]; var len = n.len; var gc = n.gc; var dv = ds.get(user); if (dv === void 0) { dv = []; ds.set(user, dv); } dv.push([counter, len, gc]); }); let keys = Array.from(ds.keys()); encoder.writeUint32(keys.length); for (var i = 0; i < keys.length; i++) { let user = keys[i]; let deletions = ds.get(user); encoder.writeVarUint(user); encoder.writeVarUint(deletions.length); for (var j = 0; j < deletions.length; j++) { let del = deletions[j]; encoder.writeVarUint(del[0]); encoder.writeVarUint(del[1]); encoder.writeUint8(del[2] ? 1 : 0); } } } * isDeleted (id) { var n = yield * this.ds.findWithUpperBound(id); return n != null && n.id[0] === id[0] && id[1] < n.id[1] + n.len } * setOperation (op) { yield * this.os.put(op); return op } * addOperation (op) { yield * this.os.put(op); // case op is created by this user, op is already broadcasted in applyCreatedOperations if (op.id[0] !== this.store.userId && this.store.forwardAppliedOperations && typeof op.id[1] !== 'string') { // is connected, and this is not going to be send in addOperation this.store.y.connector.broadcastOps([op]); } } // if insertion, try to combine with left insertion (if both have content property) * tryCombineWithLeft (op) { if ( op != null && op.left != null && op.content != null && op.left[0] === op.id[0] && Y.utils.compareIds(op.left, op.origin) ) { var left = yield * this.getInsertion(op.left); if (left.content != null && left.id[1] + left.content.length === op.id[1] && left.originOf.length === 1 && !left.gc && !left.deleted && !op.gc && !op.deleted ) { // combine! if (op.originOf != null) { left.originOf = op.originOf; } else { delete left.originOf; } left.content = left.content.concat(op.content); left.right = op.right; yield * this.os.delete(op.id); yield * this.setOperation(left); } } } * getInsertion (id) { var ins = yield * this.os.findWithUpperBound(id); if (ins == null) { return null } else { var len = ins.content != null ? ins.content.length : 1; // in case of opContent if (id[0] === ins.id[0] && id[1] < ins.id[1] + len) { return ins } else { return null } } } * getInsertionCleanStartEnd (id) { yield * this.getInsertionCleanStart(id); return yield * this.getInsertionCleanEnd(id) } // Return an insertion such that id is the first element of content // This function manipulates an operation, if necessary * getInsertionCleanStart (id) { var ins = yield * this.getInsertion(id); if (ins != null) { if (ins.id[1] === id[1]) { return ins } else { var left = Y.utils.copyObject(ins); ins.content = left.content.splice(id[1] - ins.id[1]); ins.id = id; var leftLid = Y.utils.getLastId(left); ins.origin = leftLid; left.originOf = [ins.id]; left.right = ins.id; ins.left = leftLid; // debugger // check yield * this.setOperation(left); yield * this.setOperation(ins); if (left.gc) { this.store.queueGarbageCollector(ins.id); } return ins } } else { return null } } // Return an insertion such that id is the last element of content // This function manipulates an operation, if necessary * getInsertionCleanEnd (id) { var ins = yield * this.getInsertion(id); if (ins != null) { if (ins.content == null || (ins.id[1] + ins.content.length - 1 === id[1])) { return ins } else { var right = Y.utils.copyObject(ins); right.content = ins.content.splice(id[1] - ins.id[1] + 1); // cut off remainder right.id = [id[0], id[1] + 1]; var insLid = Y.utils.getLastId(ins); right.origin = insLid; ins.originOf = [right.id]; ins.right = right.id; right.left = insLid; // debugger // check yield * this.setOperation(right); yield * this.setOperation(ins); if (ins.gc) { this.store.queueGarbageCollector(right.id); } return ins } } else { return null } } * getOperation (id/* :any */)/* :Transaction */ { var o = yield * this.os.find(id); if (id[0] !== 0xFFFFFF || o != null) { return o } else { // type is string // generate this operation? var comp = id[1].split('_'); if (comp.length > 1) { var struct = comp[0]; var op = Y.Struct[struct].create(id); op.type = comp[1]; yield * this.setOperation(op); return op } else { throw new Error( 'Unexpected case. Operation cannot be generated correctly!' + 'Incompatible Yjs version?' ) } } } * removeOperation (id) { yield * this.os.delete(id); } * setState (state) { var val = { id: [state.user], clock: state.clock }; yield * this.ss.put(val); } * getState (user) { var n = yield * this.ss.find([user]); var clock = n == null ? null : n.clock; if (clock == null) { clock = 0; } return { user: user, clock: clock } } * getStateVector () { var stateVector = []; yield * this.ss.iterate(this, null, null, function * (n) { stateVector.push({ user: n.id[0], clock: n.clock }); }); return stateVector } * getStateSet () { var ss = {}; yield * this.ss.iterate(this, null, null, function * (n) { ss[n.id[0]] = n.clock; }); return ss } * writeStateSet (encoder) { let lenPosition = encoder.pos; let len = 0; encoder.writeUint32(0); yield * this.ss.iterate(this, null, null, function * (n) { encoder.writeVarUint(n.id[0]); encoder.writeVarUint(n.clock); len++; }); encoder.setUint32(lenPosition, len); return len === 0 } /* Here, we make all missing operations executable for the receiving user. Notes: startSS: denotes to the SV that the remote user sent currSS: denotes to the state vector that the user should have if he applies all already sent operations (increases is each step) We face several problems: * Execute op as is won't work because ops depend on each other -> find a way so that they do not anymore * When changing left, must not go more to the left than the origin * When changing right, you have to consider that other ops may have op as their origin, this means that you must not set one of these ops as the new right (interdependencies of ops) * can't just go to the right until you find the first known operation, With currSS -> interdependency of ops is a problem With startSS -> leads to inconsistencies when two users join at the same time. Then the position depends on the order of execution -> error! Solution: -> re-create originial situation -> set op.left = op.origin (which never changes) -> set op.right to the first operation that is known (according to startSS) or to the first operation that has an origin that is not to the right of op. -> Enforces unique execution order -> happy user Improvements: TODO * Could set left to origin, or the first known operation (startSS or currSS.. ?) -> Could be necessary when I turn GC again. -> Is a bad(ish) idea because it requires more computation What we do: * Iterate over all missing operations. * When there is an operation, where the right op is known, send this op all missing ops to the left to the user * I explained above what we have to do with each operation. Here is how we do it efficiently: 1. Go to the left until you find either op.origin, or a known operation (let o denote current operation in the iteration) 2. Found a known operation -> set op.left = o, and send it to the user. stop 3. Found o = op.origin -> set op.left = op.origin, and send it to the user. start again from 1. (set op = o) 4. Found some o -> set o.right = op, o.left = o.origin, send it to the user, continue */ * getOperations (startSS) { // TODO: use bounds here! if (startSS == null) { startSS = new Map(); } var send = []; var endSV = yield * this.getStateVector(); for (let endState of endSV) { let user = endState.user; if (user === 0xFFFFFF) { continue } let startPos = startSS.get(user) || 0; if (startPos > 0) { // There is a change that [user, startPos] is in a composed Insertion (with a smaller counter) // find out if that is the case let firstMissing = yield * this.getInsertion([user, startPos]); if (firstMissing != null) { // update startPos startPos = firstMissing.id[1]; } } startSS.set(user, startPos); } for (let endState of endSV) { let user = endState.user; let startPos = startSS.get(user); if (user === 0xFFFFFF) { continue } yield * this.os.iterate(this, [user, startPos], [user, Number.MAX_VALUE], function * (op) { op = Y.Struct[op.struct].encode(op); if (op.struct !== 'Insert') { send.push(op); } else if (op.right == null || op.right[1] < (startSS.get(op.right[0]) || 0)) { // case 1. op.right is known // this case is only reached if op.right is known. // => this is not called for op.left, as op.right is unknown let o = op; // Remember: ? // -> set op.right // 1. to the first operation that is known (according to startSS) // 2. or to the first operation that has an origin that is not to the // right of op. // For this we maintain a list of ops which origins are not found yet. var missingOrigins = [op]; var newright = op.right; while (true) { if (o.left == null) { op.left = null; send.push(op); /* not necessary, as o is already sent.. if (!Y.utils.compareIds(o.id, op.id) && o.id[1] >= (startSS.get(o.id[0]) || 0)) { // o is not op && o is unknown o = Y.Struct[op.struct].encode(o) o.right = missingOrigins[missingOrigins.length - 1].id send.push(o) } */ break } o = yield * this.getInsertion(o.left); // we set another o, check if we can reduce $missingOrigins while (missingOrigins.length > 0 && Y.utils.matchesId(o, missingOrigins[missingOrigins.length - 1].origin)) { missingOrigins.pop(); } if (o.id[1] < (startSS.get(o.id[0]) || 0)) { // case 2. o is known op.left = Y.utils.getLastId(o); send.push(op); break } else if (Y.utils.matchesId(o, op.origin)) { // case 3. o is op.origin op.left = op.origin; send.push(op); op = Y.Struct[op.struct].encode(o); op.right = newright; if (missingOrigins.length > 0) { throw new Error( 'Reached inconsistent OS state.' + 'Operations are not correctly connected.' ) } missingOrigins = [op]; } else { // case 4. send o, continue to find op.origin var s = Y.Struct[op.struct].encode(o); s.right = missingOrigins[missingOrigins.length - 1].id; s.left = s.origin; send.push(s); missingOrigins.push(o); } } } }); } return send.reverse() } * writeOperations (encoder, decoder) { let ss = new Map(); let ssLength = decoder.readUint32(); for (let i = 0; i < ssLength; i++) { let user = decoder.readVarUint(); let clock = decoder.readVarUint(); ss.set(user, clock); } let ops = yield * this.getOperations(ss); encoder.writeUint32(ops.length); for (let i = 0; i < ops.length; i++) { let op = ops[i]; Y.Struct[op.struct].binaryEncode(encoder, Y.Struct[op.struct].encode(op)); } } /* * Get the plain untransformed operations from the database. * You can apply these operations using .applyOperationsUntransformed(ops) * */ * writeOperationsUntransformed (encoder) { let lenPosition = encoder.pos; let len = 0; encoder.writeUint32(0); // placeholder yield * this.os.iterate(this, null, null, function * (op) { if (op.id[0] !== 0xFFFFFF) { len++; Y.Struct[op.struct].binaryEncode(encoder, Y.Struct[op.struct].encode(op)); } }); encoder.setUint32(lenPosition, len); yield * this.writeStateSet(encoder); } * applyOperationsUntransformed (decoder) { let len = decoder.readUint32(); for (let i = 0; i < len; i++) { let op = Y.Struct.binaryDecodeOperation(decoder); yield * this.os.put(op); } yield * this.os.iterate(this, null, null, function * (op) { if (op.parent != null && op.parent[0] === 0xFFFFFF) { if (op.struct === 'Insert') { // update parents .map/start/end properties if (op.parentSub != null && op.left == null) { // op is child of Map let parent = yield * this.getOperation(op.parent); parent.map[op.parentSub] = op.id; yield * this.setOperation(parent); } else if (op.right == null || op.left == null) { let parent = yield * this.getOperation(op.parent); if (op.right == null) { parent.end = Y.utils.getLastId(op); } if (op.left == null) { parent.start = op.id; } yield * this.setOperation(parent); } } } }); let stateSetLength = decoder.readUint32(); for (let i = 0; i < stateSetLength; i++) { let user = decoder.readVarUint(); let clock = decoder.readVarUint(); yield * this.ss.put({ id: [user], clock: clock }); } } /* this is what we used before.. use this as a reference.. * makeOperationReady (startSS, op) { op = Y.Struct[op.struct].encode(op) op = Y.utils.copyObject(op) -- use copyoperation instead now! var o = op var ids = [op.id] // search for the new op.right // it is either the first known op (according to startSS) // or the o that has no origin to the right of op // (this is why we use the ids array) while (o.right != null) { var right = yield * this.getOperation(o.right) if (o.right[1] < (startSS[o.right[0]] || 0) || !ids.some(function (id) { return Y.utils.compareIds(id, right.origin) })) { break } ids.push(o.right) o = right } op.right = o.right op.left = op.origin return op } */ * flush () { yield * this.os.flush(); yield * this.ss.flush(); yield * this.ds.flush(); } } Y.Transaction = TransactionInterface; } const CDELETE = 0; const CINSERT = 1; const CLIST = 2; const CMAP = 3; /* An operation also defines the structure of a type. This is why operation and structure are used interchangeably here. It must be of the type Object. I hope to achieve some performance improvements when working on databases that support the json format. An operation must have the following properties: * encode - Encode the structure in a readable format (preferably string- todo) * decode (todo) - decode structure to json * execute - Execute the semantics of an operation. * requiredOps - Operations that are required to execute this operation. */ function extendStruct (Y) { var Struct = { binaryDecodeOperation: function (decoder) { let code = decoder.peekUint8(); if (code === CDELETE) { return Y.Struct.Delete.binaryDecode(decoder) } else if (code === CINSERT) { return Y.Struct.Insert.binaryDecode(decoder) } else if (code === CLIST) { return Y.Struct.List.binaryDecode(decoder) } else if (code === CMAP) { return Y.Struct.Map.binaryDecode(decoder) } else { throw new Error('Unable to decode operation!') } }, /* This is the only operation that is actually not a structure, because it is not stored in the OS. This is why it _does not_ have an id op = { target: Id } */ Delete: { encode: function (op) { return { target: op.target, length: op.length || 0, struct: 'Delete' } }, binaryEncode: function (encoder, op) { encoder.writeUint8(CDELETE); encoder.writeOpID(op.target); encoder.writeVarUint(op.length || 0); }, binaryDecode: function (decoder) { decoder.skip8(); return { target: decoder.readOpID(), length: decoder.readVarUint(), struct: 'Delete' } }, requiredOps: function (op) { return [] // [op.target] }, execute: function * (op) { return yield * this.deleteOperation(op.target, op.length || 1) } }, Insert: { /* { content: [any], opContent: Id, id: Id, left: Id, origin: Id, right: Id, parent: Id, parentSub: string (optional), // child of Map type } */ encode: function (op/* :Insertion */) /* :Insertion */ { // TODO: you could not send the "left" property, then you also have to // "op.left = null" in $execute or $decode var e/* :any */ = { id: op.id, left: op.left, right: op.right, origin: op.origin, parent: op.parent, struct: op.struct }; if (op.parentSub != null) { e.parentSub = op.parentSub; } if (op.hasOwnProperty('opContent')) { e.opContent = op.opContent; } else { e.content = op.content.slice(); } return e }, binaryEncode: function (encoder, op) { encoder.writeUint8(CINSERT); // compute info property let contentIsText = op.content != null && op.content.every(c => typeof c === 'string' && c.length === 1); let originIsLeft = Y.utils.compareIds(op.left, op.origin); let info = (op.parentSub != null ? 1 : 0) | (op.opContent != null ? 2 : 0) | (contentIsText ? 4 : 0) | (originIsLeft ? 8 : 0) | (op.left != null ? 16 : 0) | (op.right != null ? 32 : 0) | (op.origin != null ? 64 : 0); encoder.writeUint8(info); encoder.writeOpID(op.id); encoder.writeOpID(op.parent); if (info & 16) { encoder.writeOpID(op.left); } if (info & 32) { encoder.writeOpID(op.right); } if (!originIsLeft && info & 64) { encoder.writeOpID(op.origin); } if (info & 1) { // write parentSub encoder.writeVarString(op.parentSub); } if (info & 2) { // write opContent encoder.writeOpID(op.opContent); } else if (info & 4) { // write text encoder.writeVarString(op.content.join('')); } else { // convert to JSON and write encoder.writeVarString(JSON.stringify(op.content)); } }, binaryDecode: function (decoder) { let op = { struct: 'Insert' }; decoder.skip8(); // get info property let info = decoder.readUint8(); op.id = decoder.readOpID(); op.parent = decoder.readOpID(); if (info & 16) { op.left = decoder.readOpID(); } else { op.left = null; } if (info & 32) { op.right = decoder.readOpID(); } else { op.right = null; } if (info & 8) { // origin is left op.origin = op.left; } else if (info & 64) { op.origin = decoder.readOpID(); } else { op.origin = null; } if (info & 1) { // has parentSub op.parentSub = decoder.readVarString(); } if (info & 2) { // has opContent op.opContent = decoder.readOpID(); } else if (info & 4) { // has pure text content op.content = decoder.readVarString().split(''); } else { // has mixed content let s = decoder.readVarString(); op.content = JSON.parse(s); } return op }, requiredOps: function (op) { var ids = []; if (op.left != null) { ids.push(op.left); } if (op.right != null) { ids.push(op.right); } if (op.origin != null && !Y.utils.compareIds(op.left, op.origin)) { ids.push(op.origin); } // if (op.right == null && op.left == null) { ids.push(op.parent); if (op.opContent != null) { ids.push(op.opContent); } return ids }, getDistanceToOrigin: function * (op) { if (op.left == null) { return 0 } else { var d = 0; var o = yield * this.getInsertion(op.left); while (!Y.utils.matchesId(o, op.origin)) { d++; if (o.left == null) { break } else { o = yield * this.getInsertion(o.left); } } return d } }, /* # $this has to find a unique position between origin and the next known character # case 1: $origin equals $o.origin: the $creator parameter decides if left or right # let $OL= [o1,o2,o3,o4], whereby $this is to be inserted between o1 and o4 # o2,o3 and o4 origin is 1 (the position of o2) # there is the case that $this.creator < o2.creator, but o3.creator < $this.creator # then o2 knows o3. Since on another client $OL could be [o1,o3,o4] the problem is complex # therefore $this would be always to the right of o3 # case 2: $origin < $o.origin # if current $this insert_position > $o origin: $this ins # else $insert_position will not change # (maybe we encounter case 1 later, then this will be to the right of $o) # case 3: $origin > $o.origin # $this insert_position is to the left of $o (forever!) */ execute: function * (op) { var i; // loop counter // during this function some ops may get split into two pieces (e.g. with getInsertionCleanEnd) // We try to merge them later, if possible var tryToRemergeLater = []; if (op.origin != null) { // TODO: !== instead of != // we save in origin that op originates in it // we need that later when we eventually garbage collect origin (see transaction) var origin = yield * this.getInsertionCleanEnd(op.origin); if (origin.originOf == null) { origin.originOf = []; } origin.originOf.push(op.id); yield * this.setOperation(origin); if (origin.right != null) { tryToRemergeLater.push(origin.right); } } var distanceToOrigin = i = yield * Struct.Insert.getDistanceToOrigin.call(this, op); // most cases: 0 (starts from 0) // now we begin to insert op in the list of insertions.. var o; var parent; var start; // find o. o is the first conflicting operation if (op.left != null) { o = yield * this.getInsertionCleanEnd(op.left); if (!Y.utils.compareIds(op.left, op.origin) && o.right != null) { // only if not added previously tryToRemergeLater.push(o.right); } o = (o.right == null) ? null : yield * this.getOperation(o.right); } else { // left == null parent = yield * this.getOperation(op.parent); let startId = op.parentSub ? parent.map[op.parentSub] : parent.start; start = startId == null ? null : yield * this.getOperation(startId); o = start; } // make sure to split op.right if necessary (also add to tryCombineWithLeft) if (op.right != null) { tryToRemergeLater.push(op.right); yield * this.getInsertionCleanStart(op.right); } // handle conflicts while (true) { if (o != null && !Y.utils.compareIds(o.id, op.right)) { var oOriginDistance = yield * Struct.Insert.getDistanceToOrigin.call(this, o); if (oOriginDistance === i) { // case 1 if (o.id[0] < op.id[0]) { op.left = Y.utils.getLastId(o); distanceToOrigin = i + 1; // just ignore o.content.length, doesn't make a difference } } else if (oOriginDistance < i) { // case 2 if (i - distanceToOrigin <= oOriginDistance) { op.left = Y.utils.getLastId(o); distanceToOrigin = i + 1; // just ignore o.content.length, doesn't make a difference } } else { break } i++; if (o.right != null) { o = yield * this.getInsertion(o.right); } else { o = null; } } else { break } } // reconnect.. var left = null; var right = null; if (parent == null) { parent = yield * this.getOperation(op.parent); } // reconnect left and set right of op if (op.left != null) { left = yield * this.getInsertion(op.left); // link left op.right = left.right; left.right = op.id; yield * this.setOperation(left); } else { // set op.right from parent, if necessary op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start; } // reconnect right if (op.right != null) { // TODO: wanna connect right too? right = yield * this.getOperation(op.right); right.left = Y.utils.getLastId(op); // if right exists, and it is supposed to be gc'd. Remove it from the gc if (right.gc != null) { if (right.content != null && right.content.length > 1) { right = yield * this.getInsertionCleanEnd(right.id); } this.store.removeFromGarbageCollector(right); } yield * this.setOperation(right); } // update parents .map/start/end properties if (op.parentSub != null) { if (left == null) { parent.map[op.parentSub] = op.id; yield * this.setOperation(parent); } // is a child of a map struct. // Then also make sure that only the most left element is not deleted // We do not call the type in this case (this is what the third parameter is for) if (op.right != null) { yield * this.deleteOperation(op.right, 1, true); } if (op.left != null) { yield * this.deleteOperation(op.id, 1, true); } } else { if (right == null || left == null) { if (right == null) { parent.end = Y.utils.getLastId(op); } if (left == null) { parent.start = op.id; } yield * this.setOperation(parent); } } // try to merge original op.left and op.origin for (i = 0; i < tryToRemergeLater.length; i++) { var m = yield * this.getOperation(tryToRemergeLater[i]); yield * this.tryCombineWithLeft(m); } } }, List: { /* { start: null, end: null, struct: "List", type: "", id: this.os.getNextOpId(1) } */ create: function (id) { return { start: null, end: null, struct: 'List', id: id } }, encode: function (op) { var e = { struct: 'List', id: op.id, type: op.type }; if (op.requires != null) { debugger // TODO: this is used. adapt binarEncode/Decode!! console.warn('Note to myself: this is used. adapt binarEncode/Decode!!'); e.requires = op.requires; } if (op.info != null) { e.info = op.info; } return e }, binaryEncode: function (encoder, op) { encoder.writeUint8(CLIST); encoder.writeOpID(op.id); encoder.writeVarString(op.type); let info = op.info != null ? JSON.stringify(op.info) : ''; encoder.writeVarString(info); }, binaryDecode: function (decoder) { decoder.skip8(); let op = { id: decoder.readOpID(), type: decoder.readVarString(), struct: 'List' }; let info = decoder.readVarString(); if (info.length > 0) { op.info = JSON.parse(info); } return op }, requiredOps: function () { /* var ids = [] if (op.start != null) { ids.push(op.start) } if (op.end != null){ ids.push(op.end) } return ids */ return [] }, execute: function * (op) { op.start = null; op.end = null; }, ref: function * (op, pos) { if (op.start == null) { return null } var res = null; var o = yield * this.getOperation(op.start); while (true) { if (!o.deleted) { res = o; pos--; } if (pos >= 0 && o.right != null) { o = yield * this.getOperation(o.right); } else { break } } return res }, map: function * (o, f) { o = o.start; var res = []; while (o != null) { // TODO: change to != (at least some convention) var operation = yield * this.getOperation(o); if (!operation.deleted) { res.push(f(operation)); } o = operation.right; } return res } }, Map: { /* { map: {}, struct: "Map", type: "", id: this.os.getNextOpId(1) } */ create: function (id) { return { id: id, map: {}, struct: 'Map' } }, encode: function (op) { var e = { struct: 'Map', type: op.type, id: op.id, map: {} // overwrite map!! }; if (op.requires != null) { e.requires = op.require; // TODO: !! console.warn('requires is used! see same note above for List'); } if (op.info != null) { e.info = op.info; } return e }, binaryEncode: function (encoder, op) { encoder.writeUint8(CMAP); encoder.writeOpID(op.id); encoder.writeVarString(op.type); let info = op.info != null ? JSON.stringify(op.info) : ''; encoder.writeVarString(info); }, binaryDecode: function (decoder) { decoder.skip8(); let op = { id: decoder.readOpID(), type: decoder.readVarString(), struct: 'Map', map: {} }; let info = decoder.readVarString(); if (info.length > 0) { op.info = JSON.parse(info); } return op }, requiredOps: function () { return [] }, execute: function * () {}, /* Get a property by name */ get: function * (op, name) { var oid = op.map[name]; if (oid != null) { var res = yield * this.getOperation(oid); if (res == null || res.deleted) { return void 0 } else if (res.opContent == null) { return res.content[0] } else { return yield * this.getType(res.opContent) } } } } }; Y.Struct = Struct; } function Utils (Y) { Y.utils = { BinaryDecoder: BinaryDecoder, BinaryEncoder: BinaryEncoder }; Y.utils.bubbleEvent = function (type, event) { type.eventHandler.callEventListeners(event); event.path = []; while (type != null && type._deepEventHandler != null) { type._deepEventHandler.callEventListeners(event); var parent = null; if (type._parent != null) { parent = type.os.getType(type._parent); } if (parent != null && parent._getPathToChild != null) { event.path = [parent._getPathToChild(type._model)].concat(event.path); type = parent; } else { type = null; } } }; class NamedEventHandler { constructor () { this._eventListener = {}; } on (name, f) { if (this._eventListener[name] == null) { this._eventListener[name] = []; } this._eventListener[name].push(f); } off (name, f) { if (name == null || f == null) { throw new Error('You must specify event name and function!') } let listener = this._eventListener[name] || []; this._eventListener[name] = listener.filter(e => e !== f); } emit (name, value) { (this._eventListener[name] || []).forEach(l => l(value)); } destroy () { this._eventListener = null; } } Y.utils.NamedEventHandler = NamedEventHandler; class EventListenerHandler { constructor () { this.eventListeners = []; } destroy () { this.eventListeners = null; } /* Basic event listener boilerplate... */ addEventListener (f) { this.eventListeners.push(f); } removeEventListener (f) { this.eventListeners = this.eventListeners.filter(function (g) { return f !== g }); } removeAllEventListeners () { this.eventListeners = []; } callEventListeners (event) { for (var i = 0; i < this.eventListeners.length; i++) { try { var _event = {}; for (var name in event) { _event[name] = event[name]; } this.eventListeners[i](_event); } catch (e) { /* Your observer threw an error. This error was caught so that Yjs can ensure data consistency! In order to debug this error you have to check "Pause On Caught Exceptions" in developer tools. */ console.error(e); } } } } Y.utils.EventListenerHandler = EventListenerHandler; class EventHandler extends EventListenerHandler { /* :: waiting: Array; awaiting: number; onevent: Function; eventListeners: Array; */ /* onevent: is called when the structure changes. Note: "awaiting opertations" is used to denote operations that were prematurely called. Events for received operations can not be executed until all prematurely called operations were executed ("waiting operations") */ constructor (onevent /* : Function */) { super(); this.waiting = []; this.awaiting = 0; this.onevent = onevent; } destroy () { super.destroy(); this.waiting = null; this.onevent = null; } /* Call this when a new operation arrives. It will be executed right away if there are no waiting operations, that you prematurely executed */ receivedOp (op) { if (this.awaiting <= 0) { this.onevent(op); } else if (op.struct === 'Delete') { var self = this; var checkDelete = function checkDelete (d) { if (d.length == null) { throw new Error('This shouldn\'t happen! d.length must be defined!') } // we check if o deletes something in self.waiting // if so, we remove the deleted operation for (var w = 0; w < self.waiting.length; w++) { var i = self.waiting[w]; if (i.struct === 'Insert' && i.id[0] === d.target[0]) { var iLength = i.hasOwnProperty('content') ? i.content.length : 1; var dStart = d.target[1]; var dEnd = d.target[1] + (d.length || 1); var iStart = i.id[1]; var iEnd = i.id[1] + iLength; // Check if they don't overlap if (iEnd <= dStart || dEnd <= iStart) { // no overlapping continue } // we check all overlapping cases. All cases: /* 1) iiiii ddddd --> modify i and d 2) iiiiiii ddddd --> modify i, remove d 3) iiiiiii ddd --> remove d, modify i, and create another i (for the right hand side) 4) iiiii ddddddd --> remove i, modify d 5) iiiiiii ddddddd --> remove both i and d (**) 6) iiiiiii ddddd --> modify i, remove d 7) iii ddddddd --> remove i, create and apply two d with checkDelete(d) (**) 8) iiiii ddddddd --> remove i, modify d (**) 9) iiiii ddddd --> modify i and d (**) (also check if i contains content or type) */ // TODO: I left some debugger statements, because I want to debug all cases once in production. REMEMBER END TODO if (iStart < dStart) { if (dStart < iEnd) { if (iEnd < dEnd) { // Case 1 // remove the right part of i's content i.content.splice(dStart - iStart); // remove the start of d's deletion d.length = dEnd - iEnd; d.target = [d.target[0], iEnd]; continue } else if (iEnd === dEnd) { // Case 2 i.content.splice(dStart - iStart); // remove d, we do that by simply ending this function return } else { // (dEnd < iEnd) // Case 3 var newI = { id: [i.id[0], dEnd], content: i.content.slice(dEnd - iStart), struct: 'Insert' }; self.waiting.push(newI); i.content.splice(dStart - iStart); return } } } else if (dStart === iStart) { if (iEnd < dEnd) { // Case 4 d.length = dEnd - iEnd; d.target = [d.target[0], iEnd]; i.content = []; continue } else if (iEnd === dEnd) { // Case 5 self.waiting.splice(w, 1); return } else { // (dEnd < iEnd) // Case 6 i.content = i.content.slice(dEnd - iStart); i.id = [i.id[0], dEnd]; return } } else { // (dStart < iStart) if (iStart < dEnd) { // they overlap /* 7) iii ddddddd --> remove i, create and apply two d with checkDelete(d) (**) 8) iiiii ddddddd --> remove i, modify d (**) 9) iiiii ddddd --> modify i and d */ if (iEnd < dEnd) { // Case 7 // debugger // TODO: You did not test this case yet!!!! (add the debugger here) self.waiting.splice(w, 1); checkDelete({ target: [d.target[0], dStart], length: iStart - dStart, struct: 'Delete' }); checkDelete({ target: [d.target[0], iEnd], length: iEnd - dEnd, struct: 'Delete' }); return } else if (iEnd === dEnd) { // Case 8 self.waiting.splice(w, 1); w--; d.length -= iLength; continue } else { // dEnd < iEnd // Case 9 d.length = iStart - dStart; i.content.splice(0, dEnd - iStart); i.id = [i.id[0], dEnd]; continue } } } } } // finished with remaining operations self.waiting.push(d); }; if (op.key == null) { // deletes in list checkDelete(op); } else { // deletes in map this.waiting.push(op); } } else { this.waiting.push(op); } } /* You created some operations, and you want the `onevent` function to be called right away. Received operations will not be executed untill all prematurely called operations are executed */ awaitAndPrematurelyCall (ops) { this.awaiting++; ops.map(Y.utils.copyOperation).forEach(this.onevent); } * awaitOps (transaction, f, args) { function notSoSmartSort (array) { // this function sorts insertions in a executable order var result = []; while (array.length > 0) { for (var i = 0; i < array.length; i++) { var independent = true; for (var j = 0; j < array.length; j++) { if (Y.utils.matchesId(array[j], array[i].left)) { // array[i] depends on array[j] independent = false; break } } if (independent) { result.push(array.splice(i, 1)[0]); i--; } } } return result } var before = this.waiting.length; // somehow create new operations yield * f.apply(transaction, args); // remove all appended ops / awaited ops this.waiting.splice(before); if (this.awaiting > 0) this.awaiting--; // if there are no awaited ops anymore, we can update all waiting ops, and send execute them (if there are still no awaited ops) if (this.awaiting === 0 && this.waiting.length > 0) { // update all waiting ops for (let i = 0; i < this.waiting.length; i++) { var o = this.waiting[i]; if (o.struct === 'Insert') { var _o = yield * transaction.getInsertion(o.id); if (_o.parentSub != null && _o.left != null) { // if o is an insertion of a map struc (parentSub is defined), then it shouldn't be necessary to compute left this.waiting.splice(i, 1); i--; // update index } else if (!Y.utils.compareIds(_o.id, o.id)) { // o got extended o.left = [o.id[0], o.id[1] - 1]; } else if (_o.left == null) { o.left = null; } else { // find next undeleted op var left = yield * transaction.getInsertion(_o.left); while (left.deleted != null) { if (left.left != null) { left = yield * transaction.getInsertion(left.left); } else { left = null; break } } o.left = left != null ? Y.utils.getLastId(left) : null; } } } // the previous stuff was async, so we have to check again! // We also pull changes from the bindings, if there exists such a method, this could increase awaiting too if (this._pullChanges != null) { this._pullChanges(); } if (this.awaiting === 0) { // sort by type, execute inserts first var ins = []; var dels = []; this.waiting.forEach(function (o) { if (o.struct === 'Delete') { dels.push(o); } else { ins.push(o); } }); this.waiting = []; // put in executable order ins = notSoSmartSort(ins); // this.onevent can trigger the creation of another operation // -> check if this.awaiting increased & stop computation if it does for (var i = 0; i < ins.length; i++) { if (this.awaiting === 0) { this.onevent(ins[i]); } else { this.waiting = this.waiting.concat(ins.slice(i)); break } } for (i = 0; i < dels.length; i++) { if (this.awaiting === 0) { this.onevent(dels[i]); } else { this.waiting = this.waiting.concat(dels.slice(i)); break } } } } } // TODO: Remove awaitedInserts and awaitedDeletes in favor of awaitedOps, as they are deprecated and do not always work // Do this in one of the coming releases that are breaking anyway /* Call this when you successfully awaited the execution of n Insert operations */ awaitedInserts (n) { var ops = this.waiting.splice(this.waiting.length - n); for (var oid = 0; oid < ops.length; oid++) { var op = ops[oid]; if (op.struct === 'Insert') { for (var i = this.waiting.length - 1; i >= 0; i--) { let w = this.waiting[i]; // TODO: do I handle split operations correctly here? Super unlikely, but yeah.. // Also: can this case happen? Can op be inserted in the middle of a larger op that is in $waiting? if (w.struct === 'Insert') { if (Y.utils.matchesId(w, op.left)) { // include the effect of op in w w.right = op.id; // exclude the effect of w in op op.left = w.left; } else if (Y.utils.compareIds(w.id, op.right)) { // similar.. w.left = Y.utils.getLastId(op); op.right = w.right; } } } } else { throw new Error('Expected Insert Operation!') } } this._tryCallEvents(n); } /* Call this when you successfully awaited the execution of n Delete operations */ awaitedDeletes (n, newLeft) { var ops = this.waiting.splice(this.waiting.length - n); for (var j = 0; j < ops.length; j++) { var del = ops[j]; if (del.struct === 'Delete') { if (newLeft != null) { for (var i = 0; i < this.waiting.length; i++) { let w = this.waiting[i]; // We will just care about w.left if (w.struct === 'Insert' && Y.utils.compareIds(del.target, w.left)) { w.left = newLeft; } } } } else { throw new Error('Expected Delete Operation!') } } this._tryCallEvents(n); } /* (private) Try to execute the events for the waiting operations */ _tryCallEvents () { function notSoSmartSort (array) { var result = []; while (array.length > 0) { for (var i = 0; i < array.length; i++) { var independent = true; for (var j = 0; j < array.length; j++) { if (Y.utils.matchesId(array[j], array[i].left)) { // array[i] depends on array[j] independent = false; break } } if (independent) { result.push(array.splice(i, 1)[0]); i--; } } } return result } if (this.awaiting > 0) this.awaiting--; if (this.awaiting === 0 && this.waiting.length > 0) { var ins = []; var dels = []; this.waiting.forEach(function (o) { if (o.struct === 'Delete') { dels.push(o); } else { ins.push(o); } }); ins = notSoSmartSort(ins); ins.forEach(this.onevent); dels.forEach(this.onevent); this.waiting = []; } } } Y.utils.EventHandler = EventHandler; /* Default class of custom types! */ class CustomType { getPath () { var parent = null; if (this._parent != null) { parent = this.os.getType(this._parent); } if (parent != null && parent._getPathToChild != null) { var firstKey = parent._getPathToChild(this._model); var parentKeys = parent.getPath(); parentKeys.push(firstKey); return parentKeys } else { return [] } } } Y.utils.CustomType = CustomType; /* A wrapper for the definition of a custom type. Every custom type must have three properties: * struct - Structname of this type * initType - Given a model, creates a custom type * class - the constructor of the custom type (e.g. in order to inherit from a type) */ class CustomTypeDefinition { // eslint-disable-line /* :: struct: any; initType: any; class: Function; name: String; */ constructor (def) { if (def.struct == null || def.initType == null || def.class == null || def.name == null || def.createType == null ) { throw new Error('Custom type was not initialized correctly!') } this.struct = def.struct; this.initType = def.initType; this.createType = def.createType; this.class = def.class; this.name = def.name; if (def.appendAdditionalInfo != null) { this.appendAdditionalInfo = def.appendAdditionalInfo; } this.parseArguments = (def.parseArguments || function () { return [this] }).bind(this); this.parseArguments.typeDefinition = this; } } Y.utils.CustomTypeDefinition = CustomTypeDefinition; Y.utils.isTypeDefinition = function isTypeDefinition (v) { if (v != null) { if (v instanceof Y.utils.CustomTypeDefinition) return [v] else if (v.constructor === Array && v[0] instanceof Y.utils.CustomTypeDefinition) return v else if (v instanceof Function && v.typeDefinition instanceof Y.utils.CustomTypeDefinition) return [v.typeDefinition] } return false }; /* Make a flat copy of an object (just copy properties) */ function copyObject (o) { var c = {}; for (var key in o) { c[key] = o[key]; } return c } Y.utils.copyObject = copyObject; /* Copy an operation, so that it can be manipulated. Note: You must not change subproperties (except o.content)! */ function copyOperation (o) { o = copyObject(o); if (o.content != null) { o.content = o.content.map(function (c) { return c }); } return o } Y.utils.copyOperation = copyOperation; /* Defines a smaller relation on Id's */ function smaller (a, b) { return a[0] < b[0] || (a[0] === b[0] && (a[1] < b[1] || typeof a[1] < typeof b[1])) } Y.utils.smaller = smaller; function inDeletionRange (del, ins) { return del.target[0] === ins[0] && del.target[1] <= ins[1] && ins[1] < del.target[1] + (del.length || 1) } Y.utils.inDeletionRange = inDeletionRange; function compareIds (id1, id2) { if (id1 == null || id2 == null) { return id1 === id2 } else { return id1[0] === id2[0] && id1[1] === id2[1] } } Y.utils.compareIds = compareIds; function matchesId (op, id) { if (id == null || op == null) { return id === op } else { if (id[0] === op.id[0]) { if (op.content == null) { return id[1] === op.id[1] } else { return id[1] >= op.id[1] && id[1] < op.id[1] + op.content.length } } } return false } Y.utils.matchesId = matchesId; function getLastId (op) { if (op.content == null || op.content.length === 1) { return op.id } else { return [op.id[0], op.id[1] + op.content.length - 1] } } Y.utils.getLastId = getLastId; function createEmptyOpsArray (n) { var a = new Array(n); for (var i = 0; i < a.length; i++) { a[i] = { id: [null, null] }; } return a } function createSmallLookupBuffer (Store) { /* This buffer implements a very small buffer that temporarily stores operations after they are read / before they are written. The buffer basically implements FIFO. Often requested lookups will be re-queued every time they are looked up / written. It can speed up lookups on Operation Stores and State Stores. But it does not require notable use of memory or processing power. Good for os and ss, bot not for ds (because it often uses methods that require a flush) I tried to optimize this for performance, therefore no highlevel operations. */ class SmallLookupBuffer extends Store { constructor (arg1, arg2) { // super(...arguments) -- do this when this is supported by stable nodejs super(arg1, arg2); this.writeBuffer = createEmptyOpsArray(5); this.readBuffer = createEmptyOpsArray(10); } * find (id, noSuperCall) { var i, r; for (i = this.readBuffer.length - 1; i >= 0; i--) { r = this.readBuffer[i]; // we don't have to use compareids, because id is always defined! if (r.id[1] === id[1] && r.id[0] === id[0]) { // found r // move r to the end of readBuffer for (; i < this.readBuffer.length - 1; i++) { this.readBuffer[i] = this.readBuffer[i + 1]; } this.readBuffer[this.readBuffer.length - 1] = r; return r } } var o; for (i = this.writeBuffer.length - 1; i >= 0; i--) { r = this.writeBuffer[i]; if (r.id[1] === id[1] && r.id[0] === id[0]) { o = r; break } } if (i < 0 && noSuperCall === undefined) { // did not reach break in last loop // read id and put it to the end of readBuffer o = yield * super.find(id); } if (o != null) { for (i = 0; i < this.readBuffer.length - 1; i++) { this.readBuffer[i] = this.readBuffer[i + 1]; } this.readBuffer[this.readBuffer.length - 1] = o; } return o } * put (o) { var id = o.id; var i, r; // helper variables for (i = this.writeBuffer.length - 1; i >= 0; i--) { r = this.writeBuffer[i]; if (r.id[1] === id[1] && r.id[0] === id[0]) { // is already in buffer // forget r, and move o to the end of writeBuffer for (; i < this.writeBuffer.length - 1; i++) { this.writeBuffer[i] = this.writeBuffer[i + 1]; } this.writeBuffer[this.writeBuffer.length - 1] = o; break } } if (i < 0) { // did not reach break in last loop // write writeBuffer[0] var write = this.writeBuffer[0]; if (write.id[0] !== null) { yield * super.put(write); } // put o to the end of writeBuffer for (i = 0; i < this.writeBuffer.length - 1; i++) { this.writeBuffer[i] = this.writeBuffer[i + 1]; } this.writeBuffer[this.writeBuffer.length - 1] = o; } // check readBuffer for every occurence of o.id, overwrite if found // whether found or not, we'll append o to the readbuffer for (i = 0; i < this.readBuffer.length - 1; i++) { r = this.readBuffer[i + 1]; if (r.id[1] === id[1] && r.id[0] === id[0]) { this.readBuffer[i] = o; } else { this.readBuffer[i] = r; } } this.readBuffer[this.readBuffer.length - 1] = o; } * delete (id) { var i, r; for (i = 0; i < this.readBuffer.length; i++) { r = this.readBuffer[i]; if (r.id[1] === id[1] && r.id[0] === id[0]) { this.readBuffer[i] = { id: [null, null] }; } } yield * this.flush(); yield * super.delete(id); } * findWithLowerBound (id) { var o = yield * this.find(id, true); if (o != null) { return o } else { yield * this.flush(); return yield * super.findWithLowerBound.apply(this, arguments) } } * findWithUpperBound (id) { var o = yield * this.find(id, true); if (o != null) { return o } else { yield * this.flush(); return yield * super.findWithUpperBound.apply(this, arguments) } } * findNext () { yield * this.flush(); return yield * super.findNext.apply(this, arguments) } * findPrev () { yield * this.flush(); return yield * super.findPrev.apply(this, arguments) } * iterate () { yield * this.flush(); yield * super.iterate.apply(this, arguments); } * flush () { for (var i = 0; i < this.writeBuffer.length; i++) { var write = this.writeBuffer[i]; if (write.id[0] !== null) { yield * super.put(write); this.writeBuffer[i] = { id: [null, null] }; } } } } return SmallLookupBuffer } Y.utils.createSmallLookupBuffer = createSmallLookupBuffer; function generateUserId () { if (typeof crypto !== 'undefined' && crypto.getRandomValue != null) { // browser let arr = new Uint32Array(1); crypto.getRandomValues(arr); return arr[0] } else if (typeof crypto !== 'undefined' && crypto.randomBytes != null) { // node let buf = crypto.randomBytes(4); return new Uint32Array(buf.buffer)[0] } else { return Math.ceil(Math.random() * 0xFFFFFFFF) } } Y.utils.generateUserId = generateUserId; } /** * Helpers. */ var s = 1000; var m = s * 60; var h = m * 60; var d = h * 24; var y = d * 365.25; /** * Parse or format the given `val`. * * Options: * * - `long` verbose formatting [false] * * @param {String|Number} val * @param {Object} [options] * @throws {Error} throw an error if val is not a non-empty string or a number * @return {String|Number} * @api public */ var index$1$1 = function(val, options) { options = options || {}; var type = typeof val; if (type === 'string' && val.length > 0) { return parse$1(val); } else if (type === 'number' && isNaN(val) === false) { return options.long ? fmtLong(val) : fmtShort(val); } throw new Error( 'val is not a non-empty string or a valid number. val=' + JSON.stringify(val) ); }; /** * Parse the given `str` and return milliseconds. * * @param {String} str * @return {Number} * @api private */ function parse$1(str) { str = String(str); if (str.length > 100) { return; } var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( str ); if (!match) { return; } var n = parseFloat(match[1]); var type = (match[2] || 'ms').toLowerCase(); switch (type) { case 'years': case 'year': case 'yrs': case 'yr': case 'y': return n * y; case 'days': case 'day': case 'd': return n * d; case 'hours': case 'hour': case 'hrs': case 'hr': case 'h': return n * h; case 'minutes': case 'minute': case 'mins': case 'min': case 'm': return n * m; case 'seconds': case 'second': case 'secs': case 'sec': case 's': return n * s; case 'milliseconds': case 'millisecond': case 'msecs': case 'msec': case 'ms': return n; default: return undefined; } } /** * Short format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function fmtShort(ms) { if (ms >= d) { return Math.round(ms / d) + 'd'; } if (ms >= h) { return Math.round(ms / h) + 'h'; } if (ms >= m) { return Math.round(ms / m) + 'm'; } if (ms >= s) { return Math.round(ms / s) + 's'; } return ms + 'ms'; } /** * Long format for `ms`. * * @param {Number} ms * @return {String} * @api private */ function fmtLong(ms) { return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms'; } /** * Pluralization helper. */ function plural(ms, n, name) { if (ms < n) { return; } if (ms < n * 1.5) { return Math.floor(ms / n) + ' ' + name; } return Math.ceil(ms / n) + ' ' + name + 's'; } var debug$1 = createCommonjsModule$1(function (module, exports) { /** * This is the common logic for both the Node.js and web browser * implementations of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; exports.humanize = index$1$1; /** * The currently active debug mode names, and names to skip. */ exports.names = []; exports.skips = []; /** * Map of special "%n" handling functions, for the debug "format" argument. * * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". */ exports.formatters = {}; /** * Previous log timestamp. */ var prevTime; /** * Select a color. * @param {String} namespace * @return {Number} * @api private */ function selectColor(namespace) { var hash = 0, i; for (i in namespace) { hash = ((hash << 5) - hash) + namespace.charCodeAt(i); hash |= 0; // Convert to 32bit integer } return exports.colors[Math.abs(hash) % exports.colors.length]; } /** * Create a debugger with the given `namespace`. * * @param {String} namespace * @return {Function} * @api public */ function createDebug(namespace) { function debug() { // disabled? if (!debug.enabled) return; var self = debug; // set `diff` timestamp var curr = +new Date(); var ms = curr - (prevTime || curr); self.diff = ms; self.prev = prevTime; self.curr = curr; prevTime = curr; // turn the `arguments` into a proper Array var args = new Array(arguments.length); for (var i = 0; i < args.length; i++) { args[i] = arguments[i]; } args[0] = exports.coerce(args[0]); if ('string' !== typeof args[0]) { // anything else let's inspect with %O args.unshift('%O'); } // apply any `formatters` transformations var index = 0; args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { // if we encounter an escaped % then don't increase the array index if (match === '%%') return match; index++; var formatter = exports.formatters[format]; if ('function' === typeof formatter) { var val = args[index]; match = formatter.call(self, val); // now we need to remove `args[index]` since it's inlined in the `format` args.splice(index, 1); index--; } return match; }); // apply env-specific formatting (colors, etc.) exports.formatArgs.call(self, args); var logFn = debug.log || exports.log || console.log.bind(console); logFn.apply(self, args); } debug.namespace = namespace; debug.enabled = exports.enabled(namespace); debug.useColors = exports.useColors(); debug.color = selectColor(namespace); // env-specific initialization logic for debug instances if ('function' === typeof exports.init) { exports.init(debug); } return debug; } /** * Enables a debug mode by namespaces. This can include modes * separated by a colon and wildcards. * * @param {String} namespaces * @api public */ function enable(namespaces) { exports.save(namespaces); exports.names = []; exports.skips = []; var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); var len = split.length; for (var i = 0; i < len; i++) { if (!split[i]) continue; // ignore empty strings namespaces = split[i].replace(/\*/g, '.*?'); if (namespaces[0] === '-') { exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); } else { exports.names.push(new RegExp('^' + namespaces + '$')); } } } /** * Disable debug output. * * @api public */ function disable() { exports.enable(''); } /** * Returns true if the given mode name is enabled, false otherwise. * * @param {String} name * @return {Boolean} * @api public */ function enabled(name) { var i, len; for (i = 0, len = exports.skips.length; i < len; i++) { if (exports.skips[i].test(name)) { return false; } } for (i = 0, len = exports.names.length; i < len; i++) { if (exports.names[i].test(name)) { return true; } } return false; } /** * Coerce `val`. * * @param {Mixed} val * @return {Mixed} * @api private */ function coerce(val) { if (val instanceof Error) return val.stack || val.message; return val; } }); var browser = createCommonjsModule$1(function (module, exports) { /** * This is the web browser implementation of `debug()`. * * Expose `debug()` as the module. */ exports = module.exports = debug$1; exports.log = log; exports.formatArgs = formatArgs; exports.save = save; exports.load = load; exports.useColors = useColors; exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage(); /** * Colors. */ exports.colors = [ 'lightseagreen', 'forestgreen', 'goldenrod', 'dodgerblue', 'darkorchid', 'crimson' ]; /** * Currently only WebKit-based Web Inspectors, Firefox >= v31, * and the Firebug extension (any Firefox version) are known * to support "%c" CSS customizations. * * TODO: add a `localStorage` variable to explicitly enable/disable colors */ function useColors() { // NB: In an Electron preload script, document will be defined but not fully // initialized. Since we know we're in Chrome, we'll just detect this case // explicitly if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { return true; } // is webkit? http://stackoverflow.com/a/16459606/376773 // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || // is firebug? http://stackoverflow.com/a/398120/376773 (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || // is firefox >= v31? // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || // double check webkit in userAgent just in case we are in a worker (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); } /** * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. */ exports.formatters.j = function(v) { try { return JSON.stringify(v); } catch (err) { return '[UnexpectedJSONParseError]: ' + err.message; } }; /** * Colorize log arguments if enabled. * * @api public */ function formatArgs(args) { var useColors = this.useColors; args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff); if (!useColors) return; var c = 'color: ' + this.color; args.splice(1, 0, c, 'color: inherit'); // the final "%c" is somewhat tricky, because there could be other // arguments passed either before or after the %c, so we need to // figure out the correct index to insert the CSS into var index = 0; var lastC = 0; args[0].replace(/%[a-zA-Z%]/g, function(match) { if ('%%' === match) return; index++; if ('%c' === match) { // we only are interested in the *last* %c // (the user may have provided their own) lastC = index; } }); args.splice(lastC, 0, c); } /** * Invokes `console.log()` when available. * No-op when `console.log` is not a "function". * * @api public */ function log() { // this hackery is required for IE8/9, where // the `console.log` function doesn't have 'apply' return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments); } /** * Save `namespaces`. * * @param {String} namespaces * @api private */ function save(namespaces) { try { if (null == namespaces) { exports.storage.removeItem('debug'); } else { exports.storage.debug = namespaces; } } catch(e) {} } /** * Load `namespaces`. * * @return {String} returns the previously persisted debug modes * @api private */ function load() { var r; try { r = exports.storage.debug; } catch(e) {} // If debug isn't set in LS, and we're in Electron, try to load $DEBUG if (!r && typeof process !== 'undefined' && 'env' in process) { r = process.env.DEBUG; } return r; } /** * Enable namespaces listed in `localStorage.debug` initially. */ exports.enable(load()); /** * Localstorage attempts to return the localstorage. * * This is necessary because safari throws * when a user disables cookies/localstorage * and you attempt to access it. * * @return {LocalStorage} * @api private */ function localstorage() { try { return window.localStorage; } catch (e) {} } }); extendConnector(Y); extendDatabase(Y); extendTransaction(Y); extendStruct(Y); Utils(Y); Y.debug = browser; browser.formatters.Y = formatYjsMessage; browser.formatters.y = formatYjsMessageType; var requiringModules = {}; Y.requiringModules = requiringModules; Y.extend = function (name, value) { if (arguments.length === 2 && typeof name === 'string') { if (value instanceof Y.utils.CustomTypeDefinition) { Y[name] = value.parseArguments; } else { Y[name] = value; } if (requiringModules[name] != null) { requiringModules[name].resolve(); delete requiringModules[name]; } } else { for (var i = 0; i < arguments.length; i++) { var f = arguments[i]; if (typeof f === 'function') { f(Y); } else { throw new Error('Expected function!') } } } }; Y.requestModules = requestModules; function requestModules (modules) { var sourceDir; if (Y.sourceDir === null) { sourceDir = null; } else { sourceDir = Y.sourceDir || '/bower_components'; } // determine if this module was compiled for es5 or es6 (y.js vs. y.es6) // if Insert.execute is a Function, then it isnt a generator.. // then load the es5(.js) files.. var extention = typeof regeneratorRuntime !== 'undefined' ? '.js' : '.es6'; var promises = []; for (var i = 0; i < modules.length; i++) { var module = modules[i].split('(')[0]; var modulename = 'y-' + module.toLowerCase(); if (Y[module] == null) { if (requiringModules[module] == null) { // module does not exist if (typeof window !== 'undefined' && window.Y !== 'undefined') { if (sourceDir != null) { var imported = document.createElement('script'); imported.src = sourceDir + '/' + modulename + '/' + modulename + extention; document.head.appendChild(imported); } let requireModule = {}; requiringModules[module] = requireModule; requireModule.promise = new Promise(function (resolve) { requireModule.resolve = resolve; }); promises.push(requireModule.promise); } else { console.info('YJS: Please do not depend on automatic requiring of modules anymore! Extend modules as follows `require(\'y-modulename\')(Y)`'); require(modulename)(Y); } } else { promises.push(requiringModules[modules[i]].promise); } } } return Promise.all(promises) } /* :: type MemoryOptions = { name: 'memory' } type IndexedDBOptions = { name: 'indexeddb', namespace: string } type DbOptions = MemoryOptions | IndexedDBOptions type WebRTCOptions = { name: 'webrtc', room: string } type WebsocketsClientOptions = { name: 'websockets-client', room: string } type ConnectionOptions = WebRTCOptions | WebsocketsClientOptions type YOptions = { connector: ConnectionOptions, db: DbOptions, types: Array, sourceDir: string, share: {[key: string]: TypeName} } */ function Y (opts/* :YOptions */) /* :Promise */ { if (opts.hasOwnProperty('sourceDir')) { Y.sourceDir = opts.sourceDir; } opts.types = opts.types != null ? opts.types : []; var modules = [opts.db.name, opts.connector.name].concat(opts.types); for (var name in opts.share) { modules.push(opts.share[name]); } return new Promise(function (resolve, reject) { if (opts == null) reject(new Error('An options object is expected!')); else if (opts.connector == null) reject(new Error('You must specify a connector! (missing connector property)')); else if (opts.connector.name == null) reject(new Error('You must specify connector name! (missing connector.name property)')); else if (opts.db == null) reject(new Error('You must specify a database! (missing db property)')); else if (opts.connector.name == null) reject(new Error('You must specify db name! (missing db.name property)')); else { opts = Y.utils.copyObject(opts); opts.connector = Y.utils.copyObject(opts.connector); opts.db = Y.utils.copyObject(opts.db); opts.share = Y.utils.copyObject(opts.share); Y.requestModules(modules).then(function () { var yconfig = new YConfig(opts); yconfig.db.whenUserIdSet(function () { yconfig.init(function () { resolve(yconfig); }); }); }).catch(reject); } }) } class YConfig extends Y.utils.NamedEventHandler { /* :: db: Y.AbstractDatabase; connector: Y.AbstractConnector; share: {[key: string]: any}; options: Object; */ constructor (opts, callback) { super(); this.options = opts; this.db = new Y[opts.db.name](this, opts.db); this.connector = new Y[opts.connector.name](this, opts.connector); this.connected = true; } init (callback) { var opts = this.options; var share = {}; this.share = share; this.db.requestTransaction(function * requestTransaction () { // create shared object for (var propertyname in opts.share) { var typeConstructor = opts.share[propertyname].split('('); var typeName = typeConstructor.splice(0, 1); var type = Y[typeName]; var typedef = type.typeDefinition; var id = [0xFFFFFF, typedef.struct + '_' + typeName + '_' + propertyname + '_' + typeConstructor]; var args = []; if (typeConstructor.length === 1) { try { args = JSON.parse('[' + typeConstructor[0].split(')')[0] + ']'); } catch (e) { throw new Error('Was not able to parse type definition! (share.' + propertyname + ')') } if (type.typeDefinition.parseArguments == null) { throw new Error(typeName + ' does not expect arguments!') } else { args = typedef.parseArguments(args[0])[1]; } } share[propertyname] = yield * this.store.initType.call(this, id, args); } this.store.whenTransactionsFinished() .then(callback); }); } isConnected () { return this.connector.isSynced } disconnect () { if (this.connected) { this.connected = false; return this.connector.disconnect() } else { return Promise.resolve() } } reconnect () { if (!this.connected) { this.connected = true; return this.connector.reconnect() } else { return Promise.resolve() } } destroy () { var self = this; return this.close().then(function () { if (self.db.deleteDB != null) { return self.db.deleteDB() } else { return Promise.resolve() } }).then(() => { // remove existing event listener super.destroy(); }) } close () { var self = this; this.share = null; if (this.connector.destroy != null) { this.connector.destroy(); } else { this.connector.disconnect(); } return this.db.whenTransactionsFinished().then(function () { self.db.destroyTypes(); // make sure to wait for all transactions before destroying the db self.db.requestTransaction(function * () { yield * self.db.destroy(); }); return self.db.whenTransactionsFinished() }) } } function testEncoding (t, write, read, val) { let encoder = new BinaryEncoder(); write(encoder, val); let reader = new BinaryDecoder(encoder.createBuffer()); let result = read(reader); t.log(`string encode: ${JSON.stringify(val).length} bytes / binary encode: ${encoder.data.length} bytes`); t.compare(val, result, 'Compare results'); } const writeVarUint = (encoder, val) => encoder.writeVarUint(val); const readVarUint = decoder => decoder.readVarUint(); test('varUint 1 byte', async function varUint1 (t) { testEncoding(t, writeVarUint, readVarUint, 42); }); test('varUint 2 bytes', async function varUint2 (t) { testEncoding(t, writeVarUint, readVarUint, 1 << 9 | 3); testEncoding(t, writeVarUint, readVarUint, 1 << 9 | 3); }); test('varUint 3 bytes', async function varUint3 (t) { testEncoding(t, writeVarUint, readVarUint, 1 << 17 | 1 << 9 | 3); }); test('varUint 4 bytes', async function varUint4 (t) { testEncoding(t, writeVarUint, readVarUint, 1 << 25 | 1 << 17 | 1 << 9 | 3); }); test('varUint of 2839012934', async function varUint2839012934 (t) { testEncoding(t, writeVarUint, readVarUint, 2839012934); }); test('varUint random', async function varUintRandom (t) { const chance = new chance_1(t.getSeed() * Math.pow(Number.MAX_SAFE_INTEGER)); testEncoding(t, writeVarUint, readVarUint, chance.integer({min: 0, max: (1 << 28) - 1})); }); test('varUint random user id', async function varUintRandomUserId (t) { t.getSeed(); // enforces that this test is repeated testEncoding(t, writeVarUint, readVarUint, Y.utils.generateUserId()); }); const writeVarString = (encoder, val) => encoder.writeVarString(val); const readVarString = decoder => decoder.readVarString(); test('varString', async function varString (t) { testEncoding(t, writeVarString, readVarString, 'hello'); testEncoding(t, writeVarString, readVarString, 'test!'); testEncoding(t, writeVarString, readVarString, '☺☺☺'); testEncoding(t, writeVarString, readVarString, '1234'); }); test('varString random', async function varStringRandom (t) { const chance = new chance_1(t.getSeed() * 1000000000); testEncoding(t, writeVarString, readVarString, chance.string()); }); const writeDelete = Y.Struct.Delete.binaryEncode; const readDelete = Y.Struct.Delete.binaryDecode; test('encode/decode Delete operation', async function binDelete (t) { let op = { target: [10, 3000], length: 40000, struct: 'Delete' }; testEncoding(t, writeDelete, readDelete, op); }); const writeInsert = Y.Struct.Insert.binaryEncode; const readInsert = Y.Struct.Insert.binaryDecode; test('encode/decode Insert operations', async function binInsert (t) { testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: [7, 8], parent: [9, 10], struct: 'Insert', content: ['a'] }); t.log('left === origin'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: [3, 4], parent: [9, 10], struct: 'Insert', content: ['a'] }); t.log('parentsub'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: [3, 4], parent: [9, 10], parentSub: 'sub', struct: 'Insert', content: ['a'] }); t.log('opContent'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: [3, 4], parent: [9, 10], struct: 'Insert', opContent: [1000, 10000] }); t.log('mixed content'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: [3, 4], parent: [9, 10], struct: 'Insert', content: ['a', 1] }); t.log('origin is null'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: [5, 6], left: [3, 4], origin: null, parent: [9, 10], struct: 'Insert', content: ['a'] }); t.log('left = origin = right = null'); testEncoding(t, writeInsert, readInsert, { id: [1, 2], right: null, left: null, origin: null, parent: [9, 10], struct: 'Insert', content: ['a'] }); }); const writeList = Y.Struct.List.binaryEncode; const readList = Y.Struct.List.binaryDecode; test('encode/decode List operations', async function binList (t) { testEncoding(t, writeList, readList, { struct: 'List', id: [100, 33], type: 'Array' }); t.log('info is an object'); testEncoding(t, writeList, readList, { struct: 'List', id: [100, 33], type: 'Array', info: { prop: 'yay' } }); t.log('info is a string'); testEncoding(t, writeList, readList, { struct: 'List', id: [100, 33], type: 'Array', info: 'hi' }); t.log('info is a number'); testEncoding(t, writeList, readList, { struct: 'List', id: [100, 33], type: 'Array', info: 400 }); }); const writeMap = Y.Struct.Map.binaryEncode; const readMap = Y.Struct.Map.binaryDecode; test('encode/decode Map operations', async function binMap (t) { testEncoding(t, writeMap, readMap, { struct: 'Map', id: [100, 33], type: 'Map', map: {} }); t.log('info is an object'); testEncoding(t, writeMap, readMap, { struct: 'Map', id: [100, 33], type: 'Map', info: { prop: 'yay' }, map: {} }); t.log('info is a string'); testEncoding(t, writeMap, readMap, { struct: 'Map', id: [100, 33], type: 'Map', map: {}, info: 'hi' }); t.log('info is a number'); testEncoding(t, writeMap, readMap, { struct: 'Map', id: [100, 33], type: 'Map', map: {}, info: 400 }); }); Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=y.test.js.map